diff --git a/.gencode_hash.txt b/.gencode_hash.txt index 436bbf4596..9361e966b4 100644 --- a/.gencode_hash.txt +++ b/.gencode_hash.txt @@ -1,33 +1,34 @@ 23553972c4b777768f0f70fc0f89a0ec81f580b64031169a96379ea22bb95e79 gencode/docs/building_config_entity.html c21266e8de23b6e8acfac16a177db782375bf71df470c343c3f8eb8b822ef56a gencode/docs/commands_discovery.html 8aa5b5f48f97771deaf5585f8ee74c605c2a43568af04996ff8439843274cce9 gencode/docs/config.html -0dcc1e8245bf0b2791a4bf9be15d8647acd89124ade475345ce4a9c56d62a824 gencode/docs/configuration_endpoint.html -40f078753386e07bfc200eaf3c35cb4362eb413101f4f00d87c88cde3ee53080 gencode/docs/configuration_execution.html -0e2f9b56fd0d3762ff7ce14e0f9fc119cba17a3c2eeb2dcdbf1ff2fad5c6c9d1 gencode/docs/configuration_pod.html -94bb566ac6a4b6b7f89cd2cf1911f658446cc0bea405014275104623f9964dd3 gencode/docs/configuration_pubber.html +0c8f1fcfbf6de18a627f2de780ad13709ca33d52b0f4610e60e896d0856b652d gencode/docs/configuration_endpoint.html +196eed8a05e15293b43848a15a869a2fc2ac4e8a5129668c6d05590e8f7189a0 gencode/docs/configuration_execution.html +26391571e63a0d1d657e100b6aaccc695e3f041161f584ff7ed2943882d3bbba gencode/docs/configuration_pod.html +c26961bb7626fccd2ed4eec07a2d391b606b7c6c5680b7a369266f9159f68a85 gencode/docs/configuration_pubber.html 1057fa40fb7a31a23bb2773d21c38cf4590a935bd8b5ea4218e695c6204f5dd9 gencode/docs/data_template.html +86ae4ba48ebec9d368b9ad2c17d583b007bcf9082f4e511903f756f5ebadb9e8 gencode/docs/download_blobset.html 1d5294b4a1fea6555e23144e594523e4793c795069b6880ce607ad09a6015581 gencode/docs/equipment_translation.html -cc29a77c12a615e23d070c4bd6337c32f0fa057efa12df771a78c4be465aac9e gencode/docs/events.html +5fe5b52ca610e4c52bfe81bba4841d37fe243221a5f61ae719c9294f00a817aa gencode/docs/events.html 70e57ad6ef39330d958727ebf9dcd61ef6ea30e4c8653eac412bf1867fdb3a70 gencode/docs/events_alarmset.html 2734f2b1fd0d22cdc2381d728376b031aecc4a3653a1faf5f743b973d43872dc gencode/docs/events_discovery.html eafcc3c48189f605f114cde051fea9d13fc5f1a3e395d64fb0a91cb53d4c9aeb gencode/docs/events_pointset.html -b65ce6e8061e61ba676a72c624f1c425bd238e152ce8e3ebb1e9c23f0e502bcb gencode/docs/events_streams.html 81043ba732caab75704d38e6977b32b1c2bcdad89d94ad2a259d0e70ca4aa40e gencode/docs/events_system.html 46fd37b577c655965346d1e3ececf559c4384791214900eeeea1ad8fc72a02b8 gencode/docs/events_udmi.html baf59590b1a71a49e59d8f499ae5112a1b7a555294dbd72bc821203aecceefa3 gencode/docs/events_validation.html 8d77357337531f7df7b88c332d9cac934723095c5be45ca410e31c9d1f665ee1 gencode/docs/metadata.html -18178a591940c176371fb9196048eaacd40f1323997cf907ca11eb879a095c7e gencode/docs/monitoring.html +925dd4b57d66b0a6126d0ca5521e00bd40fc7dd2400976c3d522421607186360 gencode/docs/monitoring.html 64d21cf916e20566171cf9d723e59c297b576bcba89e2a73aec2b018398cc7da gencode/docs/mosquitto_client_response.html -6f332da9cc64230df3551f9748a4eaa4060146e767cae967fc0e73b1f348be5b gencode/docs/persistent_device.html +834e05bb872630fa2e01fe106294c53c9afaeea9718803872cbaf5e615d96600 gencode/docs/persistent_device.html e11595fd11477947a27461f8ef4fb6facb5f60e2abd6212193f7581ab123ff84 gencode/docs/properties.html 75d90e80269db99cb06199467c6c2483397370e15ac0392241e7e05ddf21852c gencode/docs/query_cloud.html -eb6c623142496b61e3c9b00c9ead0ba8541af2050d76ce652e1721c84aa3a096 gencode/docs/readme.md +15248b985f2edfb19c56cd18d5e5115beba0154c8e2244c2accce646a3a9cc3f gencode/docs/readme.md 2768fd01a875d1f58273e76fef9b421986824d2ab02251853840bca777b625c0 gencode/docs/schema_doc.css 165a29291d7d66653e46fd1ac5868f63e18f9d67c06ea9ebcb95d4830a528e68 gencode/docs/schema_doc.min.js 7ed934930aee763e0beebc349725ba3909115e8d346bb762f28bcbe745bb163a gencode/docs/schema_extras.js 7a1bbe014c739078d354ec484147325e1d0bd19468f275a53da38add43ebe089 gencode/docs/site_metadata.html 0993d21a1a0ae485bdb2f1794f279d13f782e841d07832f795624cc9f158ea93 gencode/docs/state.html 275171b2329d64d497fa54f8877656920be7c4f79bc90f67a0ff1bae6700399b gencode/docs/state_validation.html +8af14f0488d5c5cfc1d0eb1b00c9b06f80e459ea13b83615ad89ab9e5dde2e99 gencode/docs/upload_discovery.html 46347b31bc2a56bc74af9bb702ad40c7823887d5e9b1427903a4892cde3cd169 gencode/java/udmi/schema/AlarmAlarmsetConfig.java 520b12eb8cec4f043f81f898c4be12def09b7de138533922e17c038415fb8902 gencode/java/udmi/schema/AlarmAlarmsetEvents.java 0740aeb4462739e9be7e9c846ebc3f3e6f45f3bb752327c681aa013fbd5c2711 gencode/java/udmi/schema/AlarmAlarmsetModel.java @@ -45,6 +46,7 @@ a625c62703db143d05b3f6d828d8f0c4f730b506f1b12be12112fc0288f5fa5d gencode/java/u 387b682bde5b1b74737f2f05829b192dc9f19cc33f731b4d1ab5198a9510f5b6 gencode/java/udmi/schema/BlobBlobsetState.java c341772caa4c3b73146acac75ce21db66af9097f71a0ab85ff0e06baa4fb2ee9 gencode/java/udmi/schema/BlobUpdateTestingModel.java 378f0771197c3f6d62dbd1384e4159778969cad425a067100e7ec384cebf45e8 gencode/java/udmi/schema/BlobsetConfig.java +92457953c4cee59a66fd0c5cd7ac58467a168484de19a39da4e005794e2eb419 gencode/java/udmi/schema/BlobsetDownload.java cc9d79ed98ab76076be089c4a2bc1e85f6b3f65591548875bf706ee0c2147a72 gencode/java/udmi/schema/BlobsetState.java b4478bfc594cd2ea9631a16b828cdda3de6447263d56fc87e62fbb5a767d05e3 gencode/java/udmi/schema/BridgePodConfiguration.java d9f2924753c34ea5c8c9532d3a94c0c2fe2891d2cc8766646d40b1e90636f372 gencode/java/udmi/schema/Bucket.java @@ -69,12 +71,13 @@ a572f17a61584a32ff19968356f7f7d7bcc8430131ac3701878d0bd221e1a1fa gencode/java/u 9217b2c963003ca51ec61b9aadd40877634f2bea08d7fd412b51521e8ec61cfb gencode/java/udmi/schema/DiscoveryEvents.java af16026c143a592562b77012dd739c02364bd1a9d6bbd6c7ede4f3a5a38b7f4a gencode/java/udmi/schema/DiscoveryModel.java 0fd348086bfd8e38a54a06b0fd205f6d01fdc6236f07ccc8bef49a1832f8c767 gencode/java/udmi/schema/DiscoveryState.java +8e6b8605ed45af1bc6c30e1efb1a29fb745e7637df72d124c03907f8a0ac9325 gencode/java/udmi/schema/DiscoveryUpload.java baef666f5f58f790b73ba7bed8f60eacdd75ba3151b9abc32265954a9da84faa gencode/java/udmi/schema/Electricity.java -421126685b752587bd2e5f2ea7d7cfa140847500ed8c4f2a7146c9a605f0f2b0 gencode/java/udmi/schema/EndpointConfiguration.java +3c79394af0f0f8d6a9abd97e5fe759d606dc0a66b6b2c0b746c495600807512c gencode/java/udmi/schema/EndpointConfiguration.java 8610ae5a6a32fd17063ce7d488d4546e5ca1fd045c4cc9f0a4b94ad37f58dd60 gencode/java/udmi/schema/Entry.java d8a519092ec7401ffbf3d76d1b77bb360f4429fada288154223932ab0f9ee0cb gencode/java/udmi/schema/Enumerations.java -f890a14567657f5c97c2d3c1c753d751025ee094700ef0659f63f448529cc529 gencode/java/udmi/schema/Envelope.java -9df1e0a689390bda0847a0acab5a13fd7ced8c09b8563c13a7ce000ef1ea4999 gencode/java/udmi/schema/Events.java +09916ecaab0ae9ec10f39acdfbfe1179e74051ecc25143f695247b3a1a65de60 gencode/java/udmi/schema/Envelope.java +c6ce13a723c6b966912aab9b6deabf7e2ae6e1d74812cc3d7f15726a2fbd76c9 gencode/java/udmi/schema/Events.java 8aed152c906c51a72a9224e7aec14bcdbd9ac2e00c2b1fed6bd5515082f79488 gencode/java/udmi/schema/ExecutionConfiguration.java a5a41a2bc809988efbbc121cdf730cbc1eb066372e046747fb6ae3f30e96a8b1 gencode/java/udmi/schema/FamilyDiscovery.java 0de804fb208fdf2ef198539ed5b828b780bc6c5cdd9e31b10067372165c4b7d4 gencode/java/udmi/schema/FamilyDiscoveryConfig.java @@ -137,7 +140,6 @@ b8b388f59da9ee6951ba85b2c12e171681e6abe7c97312cf758344e0850c640c gencode/java/u 81df6abb662cbf5dfc4301d770c5e0bf72d07f228dc725b8df35339e73b90762 gencode/java/udmi/schema/StateSystemHardware.java 0110ab92d49749785dc29b92685443c895efa3cbc0637ed4374d3337d2f3be54 gencode/java/udmi/schema/StateSystemOperation.java c7e5fd53e7e1c3a439b5283f81b0555473d6b0e623aa9cdd896080b993a6f80f gencode/java/udmi/schema/States.java -5d531ecfda463512a1f6a97d0b9aa8c0e0f163f5dd63bfcfa63c406d4f820eae gencode/java/udmi/schema/StreamsEvents.java b45a8fd3c70f75bed5f3aa2285007baa6445a23819b31fe69f38c117f837bbab gencode/java/udmi/schema/Structure.java 65babc897b81d05b69e6178433b0918e513e36e861f044b68ecf4eb5f2408b37 gencode/java/udmi/schema/SystemConfig.java 53667a5822c79a1aca9345399e9dd925b761fefb96b06ff2f7c42705256a4112 gencode/java/udmi/schema/SystemDiscoveryData.java @@ -158,9 +160,9 @@ f08d327f322431a8be0e6018a7b8a9835af8e3e6ecc4ab8e18eb36810a44e5f3 gencode/java/u 225a2e3fa5a50c197ef50b55a81dc6efd33cd09a678c2c55112858f0086fd877 gencode/java/udmi/schema/ValidationSummary.java 5ec93668911c5b801e764b07ce538ab98cb8b2d211a530bce3c902c4a7773e40 gencode/java/udmi/schema/Values.java c99d2ec53e670c3b91104a4a8fbc825c1c29530a2f686fe644f977eb6b6edd89 gencode/java/udmi/schema/Water.java -f4fede5fe11244aa2391dcc6dc94c126a453bc51e292555bbc5994e52beaf27c gencode/presentation/presentation.json +275306b06e95d0c2cc9be716b904ba21243daf038069f4d869a03859c3de637f gencode/presentation/presentation.json 4cf98cbd132cde0cc8813ac35cf3712cb46014154c817c04ad2902c268cdd8fe gencode/python/pyproject.toml -f6968fad6fb525139cd063b5a9dc00a55faab999d2a429f0843ca40f54f7727b gencode/python/udmi/schema/__init__.py +cee4b5935d1b60064b5d95c64df5ff9531e0c4b19815ef39ab185836fff50417 gencode/python/udmi/schema/__init__.py f9d90861e568b27445bef241f04cce64cc44731c95c8bd9e3f65cef79d42dab0 gencode/python/udmi/schema/_base.py de7d13c346e5d98511d02b4bdccf20afeb013eaca512694ebd7f82e584b11358 gencode/python/udmi/schema/access_iot.py 32a951e2bf13f556082f8d94be079b3df6cc081b6ff59f71a82d32782cf8f8f6 gencode/python/udmi/schema/ancillary_properties.py @@ -186,7 +188,7 @@ b65e7e9152629a9175f62ed706701d02814d62d8f3e9c0b46c2ba49ad8b51077 gencode/python faf4cdb1687868fadc411c144d57a5c596af790e43df4d7ddec5ee3fd10ba4bd gencode/python/udmi/schema/config_system.py ddd6e974840a19aa75e2f322e140676f71e0715f69088b88dfea76f0625ab035 gencode/python/udmi/schema/config_system_testing.py f187d5acbf931e97365af4283ee676786b7f26294db720b1c8535ab50387a6c1 gencode/python/udmi/schema/config_udmi.py -d1cf489cbf937f801213f1425508afcc37c548e08f1da6ac755f9282bd84992d gencode/python/udmi/schema/configuration_endpoint.py +3be556292aae210f555978dee17188e2a343b7b92741a69ede1120d9074995b4 gencode/python/udmi/schema/configuration_endpoint.py 51699ab01b8d7be4db89c8e3923e0abd41a5a05e692e252deb59f10eb8189cfe gencode/python/udmi/schema/configuration_execution.py c0853ff1838a11291b53a40c43a6cdaff17951a34b3d7f3de10d9fcc7b44b79d gencode/python/udmi/schema/configuration_pod.py 62f2a07047c4cf635f2f5d021a0da433e3d38acfe9a02fcd96581aec5c1896af gencode/python/udmi/schema/configuration_pod_base.py @@ -197,16 +199,16 @@ f7193dd8eddcae6c646b99fb4c33fe47f43bed7c9615ab1bd0a0bf5612527607 gencode/python 606420089c2016cf49edd1d3b570928b404b5ac05193fd3149acaaa3fd247eb0 gencode/python/udmi/schema/discovery_family.py 86f262123525f2e6119c174fe4a4cd2f2c35863ced03a66f6db32c08e5a08542 gencode/python/udmi/schema/discovery_feature.py b978caf892bc6d0fb875b2560b81668cd1a47a09436609dc56d6353ef35b5f86 gencode/python/udmi/schema/discovery_ref.py +397067016168cf76300077d8219a74863ad0f70e455f4fe2796b94d6bf50c2af gencode/python/udmi/schema/download_blobset.py be97796ac8949ccb7fbdecfb4ff087987cf2849bf223ff39b947771a08422156 gencode/python/udmi/schema/entry.py -ab1a8ee1f12d04dd88837b6f026030b5ed3550ef3bfbbfbcb4f77cc4df39e93a gencode/python/udmi/schema/envelope.py +118a2a5de36a328dc057a784fd631a635d9710f79d208853ea02ff7c9cb245b4 gencode/python/udmi/schema/envelope.py 04f518ca330acb6f376838bdc4425f15582eb185f4b05f00fc76d1af92bac57f gencode/python/udmi/schema/equipment_translation.py -3dcf4daa94b8450b7360e58500e20e1fbe5701502ac28358112ee1a6bd2a87f1 gencode/python/udmi/schema/events.py +555d45b8882e31095e7233bfffbade89840535aff33dbe20cd24e6808b6205a3 gencode/python/udmi/schema/events.py af48a2d32b4ec326df6e3f165d8944b994fb426bd4e663c42c1d592f5cb41f83 gencode/python/udmi/schema/events_alarmset.py b58371ffa7979d1598502a35859f3010ae7a2ec2d851454c010c835e426c7a14 gencode/python/udmi/schema/events_alarmset_alarm.py 3996175098735e889730c87ffc9ce1648e6624d801694458f6d4bc147ff29741 gencode/python/udmi/schema/events_discovery.py 84700f9d8a8975130ff9c62891003f7a12f367fa7dd1ca1db5f5fae7225bf005 gencode/python/udmi/schema/events_pointset.py 9f568bd573d4992e3efa3cc3cb8cdefecb95b15675b0442873abdc7850d108f9 gencode/python/udmi/schema/events_pointset_point.py -282439e4983ae9f310fda69dd6ff979567fa983ebdefcde407df42d631fe1bad gencode/python/udmi/schema/events_streams.py e4cdab07942e52cae243b4dddbc72676794914947dd66df8add0f5969f31fd11 gencode/python/udmi/schema/events_system.py 8c5a6101e62c751714a7440db3993167b7d86bffd80589076bca1feb0a91d214 gencode/python/udmi/schema/events_udmi.py 72559779680c79dace3c5903022709852cbad7b77ed42af97debc8d852d61cb7 gencode/python/udmi/schema/events_validation.py @@ -264,3 +266,4 @@ e23b1b67fc5066ee0c9a948fbc0b31bc425d73740a8d2104b30004173f049f4a gencode/python 6c62d12f392525e4c08d3ee6105f475b941c21e0a8b7a23eaf1650d01a5b5aae gencode/python/udmi/schema/state_validation_feature.py 8a92d352337c118831e3a3b64b350a5ab248fc29150e7cdb968d5e5cf0aae30c gencode/python/udmi/schema/state_validation_schema.py 8862c955ea0aee91db532cb076112305ce6e18d9ac5b323f758fd7fcf75db7cd gencode/python/udmi/schema/state_validation_sequence.py +53a16fed02ddd4f5f9f1cd176222f563b390a76e5d346da2e32fd55ee1faa146 gencode/python/udmi/schema/upload_discovery.py diff --git a/bin/start_butler b/bin/start_butler index b3743bcbf9..65459f1e24 100755 --- a/bin/start_butler +++ b/bin/start_butler @@ -10,7 +10,7 @@ BUTLER_PID_FILE=${BUTLER_PID_FILE:-var/butler.pid} mkdir -p $(dirname "$BUTLER_PID_FILE") if [[ -f $BUTLER_PID_FILE ]]; then old_pid=$(cat $BUTLER_PID_FILE) - if ps -p $old_pid > /dev/null 2>&1; then + if [[ -n "$old_pid" && -d "/proc/$old_pid" ]]; then echo "Killing existing local butler pid $old_pid..." kill $old_pid 2>/dev/null || true sleep 1 @@ -41,7 +41,7 @@ export INFLUX_PORT=${INFLUX_PORT:-8086} # Wait for local broker and database services if running locally if [[ -d "${ETC_DIR:-var/mosquitto}" ]]; then - for i in {1..50}; do + for i in {1..300}; do if [[ -f "${ETC_DIR:-var/mosquitto}/mosquitto.ready" ]]; then break fi diff --git a/bin/start_etcd b/bin/start_etcd index a454513bc5..5dc9107bb0 100755 --- a/bin/start_etcd +++ b/bin/start_etcd @@ -31,7 +31,7 @@ mkdir -p $(dirname "$ETCD_LOG") "$ETCD_DIR" ETCD_PID_FILE=${ETCD_PID_FILE:-$ETCD_DIR/etcd.pid} if [[ -f $ETCD_PID_FILE ]]; then old_pid=$(cat $ETCD_PID_FILE) - if ps -p $old_pid > /dev/null 2>&1; then + if [[ -n "$old_pid" && -d "/proc/$old_pid" ]]; then echo Killing existing local etcd pid $old_pid... kill $old_pid || true sleep 1 diff --git a/bin/start_influx b/bin/start_influx index 3ed34c1298..130ce7ff17 100755 --- a/bin/start_influx +++ b/bin/start_influx @@ -27,7 +27,7 @@ mkdir -p $(dirname "$INFLUX_LOG") "$INFLUX_DIR" INFLUX_PID_FILE=${INFLUX_PID_FILE:-$INFLUX_DIR/influxd.pid} if [[ -f $INFLUX_PID_FILE ]]; then old_pid=$(cat $INFLUX_PID_FILE) - if ps -p $old_pid > /dev/null 2>&1; then + if [[ -n "$old_pid" && -d "/proc/$old_pid" ]]; then echo Killing existing local influxd pid $old_pid... kill $old_pid 2>/dev/null || true sleep 1 diff --git a/bin/start_mosquitto b/bin/start_mosquitto index 12331f71a4..1705aa13f3 100755 --- a/bin/start_mosquitto +++ b/bin/start_mosquitto @@ -8,7 +8,7 @@ source $UDMI_ROOT/etc/mosquitto_ctrl.sh for pid_path in "$ETC_DIR/mosquitto.pid" "var/mosquitto/mosquitto.pid" "/var/mosquitto/mosquitto.pid"; do if [[ -f "$pid_path" ]]; then OLD_PID=$(cat "$pid_path" 2>/dev/null || true) - if [[ -n "$OLD_PID" ]] && ps -p "$OLD_PID" > /dev/null 2>&1; then + if [[ -n "$OLD_PID" && -d "/proc/$OLD_PID" ]]; then echo "Killing existing local mosquitto process with PID $OLD_PID..." kill "$OLD_PID" 2>/dev/null || true sleep 1 @@ -16,8 +16,9 @@ for pid_path in "$ETC_DIR/mosquitto.pid" "var/mosquitto/mosquitto.pid" "/var/mos fi done +rm -f "$ETC_DIR/mosquitto.ready" "$UDMI_ROOT/var/mosquitto/mosquitto.ready" "/var/mosquitto/mosquitto.ready" 2>/dev/null || true + if [[ ${1:-} == stop ]]; then - rm -f "$ETC_DIR/mosquitto.ready" "$UDMI_ROOT/var/mosquitto/mosquitto.ready" "/var/mosquitto/mosquitto.ready" 2>/dev/null || true if [[ $(id -u) == 0 ]] && command -v systemctl >/dev/null 2>&1; then echo "Stopping mosquitto systemd service..." systemctl stop mosquitto 2>/dev/null || true @@ -193,7 +194,7 @@ else if timeout 1 bash -c "/dev/null; then break fi - if ! ps -p $MOSQUITTO_PID >/dev/null 2>&1; then + if [[ ! -d /proc/$MOSQUITTO_PID ]]; then break fi sleep 0.2 @@ -202,6 +203,13 @@ else echo Completed mosquitto startup. fi +for i in $(seq 1 50); do + if $MOSQUITTO_CTRL listClients >/dev/null 2>&1; then + break + fi + sleep 0.2 +done + $MOSQUITTO_CTRL createRole service || true $MOSQUITTO_CTRL addRoleACL service subscribePattern '/r/+/d/+/#' allow || true $MOSQUITTO_CTRL addRoleACL service subscribePattern '$share/+//r/+/d/+/#' allow || true @@ -210,6 +218,10 @@ $MOSQUITTO_CTRL addRoleACL service subscribePattern '/uufi/#' allow || true $MOSQUITTO_CTRL addRoleACL service publishClientSend '/uufi/#' allow || true $MOSQUITTO_CTRL addRoleACL service subscribePattern 'events/#' allow || true $MOSQUITTO_CTRL addRoleACL service publishClientSend 'events/#' allow || true +$MOSQUITTO_CTRL addRoleACL service subscribePattern 'upload/#' allow || true +$MOSQUITTO_CTRL addRoleACL service publishClientSend 'upload/#' allow || true +$MOSQUITTO_CTRL addRoleACL service subscribePattern 'download/#' allow || true +$MOSQUITTO_CTRL addRoleACL service publishClientSend 'download/#' allow || true $MOSQUITTO_CTRL addRoleACL service subscribePattern 'state/#' allow || true $MOSQUITTO_CTRL addRoleACL service publishClientSend 'state/#' allow || true $MOSQUITTO_CTRL addRoleACL service subscribePattern 'state' allow || true diff --git a/bin/start_udmis b/bin/start_udmis index a62f666462..005e2f64b6 100755 --- a/bin/start_udmis +++ b/bin/start_udmis @@ -10,7 +10,7 @@ mkdir -p $(dirname "$UDMIS_PID_FILE") if [[ ${1:-} == stop ]]; then if [[ -f $UDMIS_PID_FILE ]]; then OLD_PID=$(cat $UDMIS_PID_FILE) - if ps -p $OLD_PID > /dev/null 2>&1; then + if [[ -n "$OLD_PID" && -d "/proc/$OLD_PID" ]]; then echo "Killing old udmis process $OLD_PID from local PID file..." kill $OLD_PID 2>/dev/null || true sleep 1 @@ -21,7 +21,7 @@ fi if [[ -f $UDMIS_PID_FILE ]]; then OLD_PID=$(cat $UDMIS_PID_FILE) - if ps -p $OLD_PID > /dev/null 2>&1; then + if [[ -n "$OLD_PID" && -d "/proc/$OLD_PID" ]]; then echo "Killing old udmis process $OLD_PID from local PID file..." kill $OLD_PID 2>/dev/null || true sleep 2 @@ -118,7 +118,7 @@ fi # Wait for prerequisite services (mosquitto and etcd) if running locally if [[ -d "$ETC_DIR" ]]; then - for i in $(seq 1 50); do + for i in $(seq 1 300); do if [[ -f "$ETC_DIR/mosquitto.ready" ]]; then break fi @@ -166,7 +166,7 @@ if [[ ! -f $UDMI_POD_READY ]]; then echo "=== DIAGNOSTIC LOGS FOR UDMIS STARTUP FAILURE ===" echo "Host working directory: $PWD" echo "Process $PID status:" - ps -p $PID || echo "Process $PID not running" + [[ -d "/proc/$PID" ]] && (ps -p $PID 2>/dev/null || ps | grep -E "^\s*${PID}\s" || echo "Process $PID running") || echo "Process $PID not running" echo "Last 100 lines of $UDMIS_LOG:" tail -n 100 "$UDMIS_LOG" || true echo "var/ directory contents:" diff --git a/bin/test_mosquitto b/bin/test_mosquitto index 14c94a633d..f8b07d4937 100755 --- a/bin/test_mosquitto +++ b/bin/test_mosquitto @@ -70,7 +70,9 @@ $MOSQUITTO_CTRL addClientRole $dev_id $role_name $MOSQUITTO_CTRL addRoleACL $role_name subscribePattern "$dev_id/config" allow $MOSQUITTO_CTRL addRoleACL $role_name subscribePattern "$dev_id/commands" allow $MOSQUITTO_CTRL addRoleACL $role_name subscribePattern "$dev_id/errors" allow +$MOSQUITTO_CTRL addRoleACL $role_name subscribePattern "$dev_id/download/#" allow $MOSQUITTO_CTRL addRoleACL $role_name publishClientSend "$dev_id/events/#" allow +$MOSQUITTO_CTRL addRoleACL $role_name publishClientSend "$dev_id/upload/#" allow $MOSQUITTO_CTRL addRoleACL $role_name publishClientSend "$dev_id/state" allow sleep 1 diff --git a/bridgehead/mosquitto/mosquitto_startup.sh b/bridgehead/mosquitto/mosquitto_startup.sh index 3d35e96349..abfbb4329c 100644 --- a/bridgehead/mosquitto/mosquitto_startup.sh +++ b/bridgehead/mosquitto/mosquitto_startup.sh @@ -21,6 +21,7 @@ registry_id=$(jq -r .registry_id $site_config) source $UDMI_ROOT/etc/mosquitto_ctrl.sh mkdir -p $CERT_DIR +rm -f $CERT_DIR/mosquitto.ready if [[ -n "$HOST_IP" ]]; then sed -i -e "s|IP:127.0.0.1,|IP:127.0.0.1, IP:${HOST_IP},|" bin/keygen @@ -36,5 +37,6 @@ $MOSQUITTO_CTRL addClientRole $SERV_USER service echo "Starting initializing site $site_model" | tee -a $UDMIS_LOG bin/mosquctl_site $site_model $MOSQUITTO_CTRL addClientRole $AUTH_USER service +touch $CERT_DIR/mosquitto.ready sleep infinity \ No newline at end of file diff --git a/bridgehead/udmis/startup/udmis_startup.sh b/bridgehead/udmis/startup/udmis_startup.sh index 859a84d2ee..5648fcc5b4 100644 --- a/bridgehead/udmis/startup/udmis_startup.sh +++ b/bridgehead/udmis/startup/udmis_startup.sh @@ -28,6 +28,10 @@ fi registry_id=$(jq -r .registry_id $site_config) sleep 25 +for i in $(seq 1 35); do + [[ -f /etc/mosquitto/certs/mosquitto.ready ]] && break + sleep 1 +done echo Starting udmis proper... | tee -a $UDMIS_LOG diff --git a/butler/bin/butler b/butler/bin/butler index f3295fc7fc..06a2339d5d 100755 --- a/butler/bin/butler +++ b/butler/bin/butler @@ -22,6 +22,7 @@ sys.path.insert(0, REPO_ROOT) from butler.src.discovery import run_discovery from butler.src.mapping import run_mapping +from butler.src.reassemble import run_reassemble from butler.src.service import ButlerService @@ -40,9 +41,9 @@ def get_registry_id(site_model: str) -> str: def main(): raw_args = sys.argv[1:] - # Check if this is a mapping/discovery command - # e.g.: butler [targets...] - mapping_commands = {"provision", "discover", "map"} + # Check if this is a mapping/discovery/reassemble command + # e.g.: butler [targets...] + mapping_commands = {"provision", "discover", "map", "reassemble"} is_mapping_cmd = False for arg in raw_args: if arg in mapping_commands: @@ -54,7 +55,7 @@ def main(): parser.add_argument("site_model") parser.add_argument("project_spec") parser.add_argument("device_id") - parser.add_argument("command", choices=["provision", "discover", "map"]) + parser.add_argument("command", choices=["provision", "discover", "map", "reassemble"]) parser.add_argument("targets", nargs="*") parser.add_argument("--butler-port", type=int, default=None, help="Butler MCP server port") args = parser.parse_args() @@ -71,6 +72,12 @@ def main(): args.targets, butler_port=args.butler_port, ) + elif args.command == "reassemble": + if len(args.targets) != 2: + raise ValueError( + "reassemble requires and target arguments" + ) + run_reassemble(args.targets[0], args.targets[1]) elif args.command == "provision": pass return diff --git a/butler/src/dispatcher.py b/butler/src/dispatcher.py index 7bcd2abbba..d0cc4dbe0c 100644 --- a/butler/src/dispatcher.py +++ b/butler/src/dispatcher.py @@ -56,6 +56,9 @@ def dispatch(self, envelope: Dict[str, Any], payload: Dict[str, Any]) -> Dict[st sub_folder = envelope.get("subFolder") sub_type = envelope.get("subType") + if sub_type == "download": + return {"target": "ignored", "table": None, "count": 0} + matched_handler = None for handler in self.handlers: if handler.can_handle(sub_folder, sub_type, payload): diff --git a/butler/src/handlers/discovery.py b/butler/src/handlers/discovery.py index 5370102172..3b83595e57 100644 --- a/butler/src/handlers/discovery.py +++ b/butler/src/handlers/discovery.py @@ -23,7 +23,7 @@ class DiscoveryHandler(BaseHandler): """Processes discovery events and saves rows into PostgreSQL udmi_discovery table.""" def can_handle(self, sub_folder: Optional[str], sub_type: Optional[str], payload: Dict[str, Any]) -> bool: - return sub_folder == "discovery" + return sub_folder == "discovery" and sub_type in ("events", None) def process_message( self, diff --git a/butler/src/reassemble.py b/butler/src/reassemble.py new file mode 100644 index 0000000000..bd04804afb --- /dev/null +++ b/butler/src/reassemble.py @@ -0,0 +1,152 @@ +"""Generic chunked upload reassembly utility for UDMI upload/* streams.""" + +import base64 +import hashlib +import json +import os +import sys +from typing import Any, Dict, List + +PCAP_MAGIC_NUMBERS = ( + b"\xa1\xb2\xc3\xd4", # Standard pcap (same-endian) + b"\xd4\xc3\xb2\xa1", # Standard pcap (swapped-endian) + b"\xa1\xb2\x3c\x4d", # Nanosecond pcap (same-endian) + b"\x4d\x3c\xb2\xa1", # Nanosecond pcap (swapped-endian) +) + + +def parse_stream_events(raw_text: str) -> List[Dict[str, Any]]: + """Parse upload chunk objects from JSON array, JSONL, or mosquitto_sub output.""" + raw_text = raw_text.strip() + if not raw_text: + return [] + + if raw_text.startswith("["): + try: + data = json.loads(raw_text) + if isinstance(data, list): + return [ + item.get("payload", item) + if isinstance(item, dict) and isinstance(item.get("payload"), dict) + else item + for item in data + if isinstance(item, dict) + ] + except json.JSONDecodeError: + pass + + events: List[Dict[str, Any]] = [] + for line in raw_text.splitlines(): + line = line.strip() + if not line: + continue + if " " in line and not line.startswith("{"): + _, _, payload = line.partition(" ") + else: + payload = line + + try: + obj = json.loads(payload) + if isinstance(obj, dict): + inner = ( + obj["payload"] + if isinstance(obj.get("payload"), dict) + and ("event_no" in obj["payload"] or "data" in obj["payload"]) + else obj + ) + if "event_no" in inner or "data" in inner or "sha256" in inner: + events.append(inner) + except json.JSONDecodeError: + continue + + return events + + +def _assemble_and_verify( + sorted_events: List[Dict[str, Any]], eof_events: List[Dict[str, Any]] +) -> bytes: + """Validates sequential event_no, decodes Base64 chunks, and checks SHA-256.""" + total_chunks = len(sorted_events) + if eof_events: + terminal_event_no = eof_events[-1].get("event_no") + if terminal_event_no is not None and int(terminal_event_no) < 0: + expected_chunks = -(int(terminal_event_no) + 1) + if expected_chunks != total_chunks: + raise ValueError( + f"Chunk count mismatch: terminal event_no {terminal_event_no} " + f"expects {expected_chunks} chunks, found {total_chunks}." + ) + + assembled_data = bytearray() + for idx, evt in enumerate(sorted_events, start=1): + event_no = evt.get("event_no") + if event_no is not None and int(event_no) != idx: + raise ValueError( + f"Non-contiguous upload chunk sequence: expected event_no {idx}, got {event_no}." + ) + try: + assembled_data.extend(base64.b64decode(evt["data"])) + except Exception as err: + raise ValueError( + f"Failed to decode base64 chunk at event_no {idx}: {err}" + ) from err + + if not assembled_data: + raise ValueError("Reassembled upload payload is empty (0 bytes).") + + if eof_events and eof_events[-1].get("sha256"): + expected_sha256 = str(eof_events[-1]["sha256"]).lower() + actual_sha256 = hashlib.sha256(assembled_data).hexdigest().lower() + if actual_sha256 != expected_sha256: + raise ValueError( + f"SHA-256 mismatch (expected {expected_sha256}, got {actual_sha256})." + ) + return bytes(assembled_data) + + +def run_reassemble(input_source: str, output_path: str) -> None: + """Reassembles chunked UDMI upload records into a binary artifact file.""" + if input_source == "-": + raw_content = sys.stdin.read() + else: + if not os.path.exists(input_source): + raise FileNotFoundError(f"Input file '{input_source}' does not exist.") + with open(input_source, "r", encoding="utf-8", errors="replace") as f: + raw_content = f.read() + + events = parse_stream_events(raw_content) + if not events: + raise ValueError("No valid upload records found in input.") + + data_events = [ + e for e in events if e.get("data") and (e.get("event_no") or 1) > 0 + ] + if not data_events: + raise ValueError("Found upload records but none contained base64 'data'.") + + eof_events = [ + e for e in events if e.get("sha256") or (e.get("event_no") or 0) < 0 + ] + sorted_events = sorted(data_events, key=lambda e: int(e.get("event_no", 0))) + assembled_data = _assemble_and_verify(sorted_events, eof_events) + + os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) + with open(output_path, "wb") as f: + f.write(assembled_data) + + magic = assembled_data[:4] + magic_status = ( + "VALID PCAP" if any(magic == m for m in PCAP_MAGIC_NUMBERS) else "BINARY" + ) + first_event = events[0] + print("========================================") + print("Upload Reassembly Completed Successfully") + print("========================================") + print( + f"Family / Gen : {first_event.get('family', 'unknown')} / " + f"{first_event.get('generation', 'unknown')}" + ) + print(f"Chunks Decoded : {len(sorted_events)}") + print(f"Total Bytes : {len(assembled_data)}") + print(f"Magic Bytes : {magic.hex()} ({magic_status})") + print(f"Output File : {output_path}") diff --git a/butler/src/service.py b/butler/src/service.py index bd6798d3c4..40548db8b8 100644 --- a/butler/src/service.py +++ b/butler/src/service.py @@ -56,7 +56,7 @@ def parse_mqtt_topic(topic: str) -> Dict[str, Optional[str]]: sub_type = parts[r_idx + 2] if r_idx + 3 < len(parts): sub_folder = parts[r_idx + 3] - elif len(parts) > 0 and parts[0] in ("events", "state", "commands", "config"): + elif len(parts) > 0 and parts[0] in ("events", "state", "commands", "config", "upload", "download"): sub_type = parts[0] if len(parts) > 1: sub_folder = parts[1] diff --git a/butler/tests/test_dispatcher.py b/butler/tests/test_dispatcher.py index c3ca0cfc5f..a57e9068bb 100644 --- a/butler/tests/test_dispatcher.py +++ b/butler/tests/test_dispatcher.py @@ -56,6 +56,56 @@ def test_dispatch_fallback(self): self.assertEqual(res["target"], "postgres") self.assertEqual(res["table"], "udmi_messages") + def test_dispatch_upload_discovery_fallback(self): + envelope = {"subFolder": "discovery", "subType": "upload", "deviceId": "DEV-1"} + payload = {"family": "bacnet", "generation": "2026-04-06T12:00:00Z", "event_no": 0} + + res = self.dispatcher.dispatch(envelope, payload) + self.assertEqual(res["target"], "postgres") + self.assertEqual(res["table"], "udmi_messages") + + def test_dispatch_download_ignored(self): + envelope = {"subFolder": "blobset", "subType": "download", "deviceId": "DEV-1"} + payload = {"blobs": {"_iot_endpoint_config": {"hostname": "mqtt.example.com"}}} + + res = self.dispatcher.dispatch(envelope, payload) + self.assertEqual(res["target"], "ignored") + self.assertEqual(res["count"], 0) + self.mock_pg.insert_raw_message.assert_not_called() + + def test_reassemble_upload(self): + import base64 + import hashlib + import json + import tempfile + from butler.src.reassemble import run_reassemble + + raw_bytes = b"\xa1\xb2\xc3\xd4PCAP-PAYLOAD" + sha256_hex = hashlib.sha256(raw_bytes).hexdigest() + lines = [ + json.dumps({"family": "ether", "generation": "2026-07-08T14:35:00Z", "event_no": 0}), + json.dumps({ + "family": "ether", + "generation": "2026-07-08T14:35:00Z", + "event_no": 1, + "data": base64.b64encode(raw_bytes).decode(), + }), + json.dumps({ + "family": "ether", + "generation": "2026-07-08T14:35:00Z", + "event_no": -2, + "sha256": sha256_hex, + }), + ] + with tempfile.TemporaryDirectory() as tmpdir: + in_path = os.path.join(tmpdir, "stream.jsonl") + out_path = os.path.join(tmpdir, "out.pcap") + with open(in_path, "w", encoding="utf-8") as f: + f.write("\n".join(lines)) + run_reassemble(in_path, out_path) + with open(out_path, "rb") as f: + self.assertEqual(f.read(), raw_bytes) + if __name__ == "__main__": unittest.main() diff --git a/butler/tests/test_handlers.py b/butler/tests/test_handlers.py index 18df34746b..f98e89620a 100644 --- a/butler/tests/test_handlers.py +++ b/butler/tests/test_handlers.py @@ -92,6 +92,8 @@ def test_system_state_handler(self): def test_discovery_handler(self): handler = DiscoveryHandler() self.assertTrue(handler.can_handle("discovery", "events", {})) + self.assertFalse(handler.can_handle("discovery", "upload", {})) + self.assertFalse(handler.can_handle("discovery", "state", {})) envelope = {"deviceId": "GAT-1", "deviceRegistryId": "reg-1"} payload = { diff --git a/clientlib/python/src/udmi/core/device.py b/clientlib/python/src/udmi/core/device.py index 6b4c5f87f5..1b9f69cedc 100644 --- a/clientlib/python/src/udmi/core/device.py +++ b/clientlib/python/src/udmi/core/device.py @@ -6,6 +6,8 @@ """ import datetime +import hashlib +import json import logging import threading import time @@ -30,14 +32,35 @@ from udmi.core.persistence import DevicePersistence from udmi.core.persistence.file_backend import FilePersistenceBackend from udmi.core.utils.file_ops import mask_secrets +from udmi.schema import BlobBlobsetConfig +from udmi.schema import BlobBlobsetState +from udmi.schema import BlobsetDownload +from udmi.schema import BlobsetState from udmi.schema import Config from udmi.schema import EndpointConfiguration +from udmi.schema import Entry from udmi.schema import Metadata from udmi.schema import State from udmi.schema import SystemState +from udmi.schema.common import Phase LOGGER = logging.getLogger(__name__) + +def _canonical_json_sha256(obj: Any) -> str: + """Computes the SHA-256 hex digest of an object's canonical terse JSON.""" + def _strip_nones(val: Any) -> Any: + if isinstance(val, dict): + return {k: _strip_nones(v) for k, v in val.items() if v is not None} + if isinstance(val, list): + return [_strip_nones(v) for v in val] + return val + + canonical = json.dumps( + _strip_nones(obj), sort_keys=True, separators=(",", ":") + ) + return hashlib.sha256(canonical.encode("utf-8")).hexdigest() + T = TypeVar("T", bound=BaseManager) MAX_CONNECTION_RETRIES = 3 STATE_THROTTLE_SEC = 2.0 @@ -139,6 +162,8 @@ def __init__(self, self._state_lock = threading.RLock() self._redirection_handler: Optional[RedirectionHandler] = None self._connection_handler: Optional[ConnectionHandler] = None + self._pending_endpoint_blob: Optional[BlobBlobsetConfig] = None + self._endpoint_blob_state: Optional[BlobBlobsetState] = None LOGGER.info("Device initialized with %s managers.", len(self.managers)) @property @@ -193,7 +218,8 @@ def _setup_message_handlers(self) -> None: return self.dispatcher.register_handler("config", self.handle_config) self.dispatcher.register_handler("commands/#", self.handle_command) - LOGGER.debug("Registered config and command handlers.") + self.dispatcher.register_handler("download/#", self.handle_download) + LOGGER.debug("Registered config, command, and download handlers.") # --- Connection Callbacks --- @@ -266,8 +292,28 @@ def handle_config(self, device_id: str, _channel: str, payload: Dict) -> None: return if self._has_new_endpoint_config(config_obj): - self._try_redirect_endpoint(config_obj) - return + blob_config = config_obj.blobset.blobs.get( + IOT_ENDPOINT_CONFIG_BLOB_KEY + ) + if blob_config and blob_config.url and blob_config.url.startswith("download://"): + LOGGER.info( + "Endpoint blob uses download:// scheme. Requesting download..." + ) + self._pending_endpoint_blob = blob_config + self._endpoint_blob_state = BlobBlobsetState( + phase=Phase.apply, + generation=blob_config.generation, + ) + else: + self._try_redirect_endpoint(config_obj) + return + elif not ( + config_obj.blobset + and config_obj.blobset.blobs + and IOT_ENDPOINT_CONFIG_BLOB_KEY in config_obj.blobset.blobs + ): + self._pending_endpoint_blob = None + self._endpoint_blob_state = None self._loop_state.config_received_event.set() self.config = config_obj @@ -305,6 +351,120 @@ def handle_command(self, device_id: str, channel: str, payload: Dict[str, Any]) LOGGER.error("Error in %s.handle_command: %s", manager.__class__.__name__, e) + def handle_download( + self, device_id: str, channel: str, payload: Dict[str, Any] + ) -> None: + """ + Orchestration method to handle a new download payload. + + Args: + device_id: The ID of the device this download is for. + channel: The full download channel (e.g., 'download/blobset'). + payload: The pre-parsed dictionary of the JSON payload. + """ + download_name = channel.split('/')[-1] + + if device_id != self.device_id: + self._route_proxy_download(device_id, download_name, payload) + return + + LOGGER.info( + "Download '%s' received for Device %s...", + download_name, + self.device_id, + ) + if download_name == "blobset": + try: + blobset_download = BlobsetDownload.from_dict(payload) + if ( + blobset_download.blobs + and IOT_ENDPOINT_CONFIG_BLOB_KEY in blobset_download.blobs + ): + self._process_endpoint_download( + blobset_download.blobs[IOT_ENDPOINT_CONFIG_BLOB_KEY], + payload.get("blobs", {}).get( + IOT_ENDPOINT_CONFIG_BLOB_KEY, {} + ), + ) + except (TypeError, ValueError) as e: + LOGGER.error("Failed to parse download/blobset message: %s", e) + + for manager in self.managers: + if hasattr(manager, "handle_download"): + try: + manager.handle_download(download_name, payload) + except (AttributeError, TypeError, KeyError, ValueError) as e: + LOGGER.error( + "Error in %s.handle_download: %s", + manager.__class__.__name__, + e, + ) + + def _process_endpoint_download( + self, new_endpoint: EndpointConfiguration, raw_blob: Dict[str, Any] + ) -> None: + """Validates and applies an EndpointConfiguration from download/blobset.""" + if self._pending_endpoint_blob is None: + LOGGER.warning( + "Ignoring unsolicited endpoint download for %s", self.device_id + ) + return + generation = self._pending_endpoint_blob.generation or "download" + expected_sha = self._pending_endpoint_blob.sha256 + error_msg = self._validate_downloaded_endpoint( + new_endpoint, raw_blob, expected_sha + ) + self._pending_endpoint_blob = None + if error_msg: + LOGGER.error("%s", error_msg) + self._endpoint_blob_state = BlobBlobsetState( + phase=Phase.final, + generation=generation, + status=Entry( + category="blobset.blob.apply", + level=500, + message=error_msg, + timestamp=datetime.datetime.now( + datetime.timezone.utc + ).isoformat(), + ), + ) + self._publish_state(bypass_throttle=True, blocking=True) + return + + self._endpoint_blob_state = BlobBlobsetState( + phase=Phase.final, + generation=generation, + ) + self._publish_state(bypass_throttle=True, blocking=True) + self._apply_redirect_endpoint(new_endpoint, generation) + + @staticmethod + def _validate_downloaded_endpoint( + new_endpoint: EndpointConfiguration, + raw_blob: Dict[str, Any], + expected_sha: Optional[str], + ) -> Optional[str]: + """Checks SHA-256 integrity and expiry on a downloaded endpoint blob.""" + if not expected_sha: + return "Missing expected SHA-256 hash for downloaded endpoint blob" + actual_sha = _canonical_json_sha256(raw_blob) + if actual_sha.lower() != expected_sha.lower(): + return ( + "Downloaded endpoint blob SHA-256 mismatch: " + f"expected {expected_sha}, got {actual_sha}" + ) + if new_endpoint.expiry: + expiry_dt = datetime.datetime.fromisoformat( + new_endpoint.expiry.replace("Z", "+00:00") + ) + if expiry_dt <= datetime.datetime.now(datetime.timezone.utc): + return ( + "Downloaded endpoint configuration is expired: " + f"{new_endpoint.expiry}" + ) + return None + # --- Proxy Routing Helpers --- def _route_proxy_config(self, device_id: str, payload: Dict) -> None: @@ -342,6 +502,33 @@ def _route_proxy_command(self, device_id: str, command_name: str, payload: Dict) if not handled: LOGGER.debug("Received command for proxy '%s' but no GatewayManager found.", device_id) + def _route_proxy_download( + self, device_id: str, download_name: str, payload: Dict + ) -> None: + """ + Routes a download message meant for a proxy device to the GatewayManager. + """ + handled = False + for manager in self.managers: + if hasattr(manager, "handle_proxy_download"): + try: + manager.handle_proxy_download( + device_id, download_name, payload + ) + handled = True + except Exception as e: # pylint: disable=broad-exception-caught + LOGGER.error( + "Error routing proxy download to %s: %s", + manager.__class__.__name__, + e, + ) + + if not handled: + LOGGER.debug( + "Received download for proxy '%s' but no GatewayManager found.", + device_id, + ) + # --- Device lifecycle and endpoint management --- def _has_new_endpoint_config(self, config: Config) -> bool: @@ -353,6 +540,12 @@ def _has_new_endpoint_config(self, config: Config) -> bool: if not blob_config: return False + if ( + self._endpoint_blob_state + and self._endpoint_blob_state.generation == blob_config.generation + ): + return False + current_gen = self.persistence.get_active_generation() if blob_config.generation and blob_config.generation != current_gen: return True @@ -372,25 +565,31 @@ def _try_redirect_endpoint(self, config: Config) -> None: EndpointConfiguration ) - LOGGER.info("Endpoint blob fetched. Generation: %s. Saving...", - generation) + self._apply_redirect_endpoint(new_endpoint, generation) - self.persistence.save_active_endpoint(new_endpoint, generation) - self.current_endpoint = new_endpoint - self.device_id = self.current_endpoint.client_id.split('/')[-1] + except Exception as e: # pylint:disable=broad-exception-caught + LOGGER.error("Failed to process endpoint redirect: %s", e) - if self._redirection_handler: - try: - LOGGER.info("Invoking redirection handler...") - self._redirection_handler(new_endpoint) - except Exception as e: # pylint: disable=broad-exception-caught - LOGGER.error("Error in redirection handler: %s", e) + def _apply_redirect_endpoint( + self, new_endpoint: EndpointConfiguration, generation: str + ) -> None: + """Saves and activates a new endpoint configuration.""" + LOGGER.info("Endpoint blob fetched. Generation: %s. Saving...", + generation) - LOGGER.info("Signaling main loop to trigger connection reset...") - self._loop_state.reset_event.set() + self.persistence.save_active_endpoint(new_endpoint, generation) + self.current_endpoint = new_endpoint + self.device_id = self.current_endpoint.client_id.split('/')[-1] - except Exception as e: # pylint:disable=broad-exception-caught - LOGGER.error("Failed to process endpoint redirect: %s", e) + if self._redirection_handler: + try: + LOGGER.info("Invoking redirection handler...") + self._redirection_handler(new_endpoint) + except Exception as e: # pylint: disable=broad-exception-caught + LOGGER.error("Error in redirection handler: %s", e) + + LOGGER.info("Signaling main loop to trigger connection reset...") + self._loop_state.reset_event.set() # --- Main Run Loop --- @@ -532,6 +731,18 @@ def _publish_state( except (AttributeError, TypeError, KeyError, ValueError) as e: LOGGER.error("Error in %s.update_state: %s", manager.__class__.__name__, e) + if self._endpoint_blob_state is not None: + if self.state.blobset is None: + self.state.blobset = BlobsetState(blobs={}) + elif self.state.blobset.blobs is None: + self.state.blobset.blobs = {} + self.state.blobset.blobs[IOT_ENDPOINT_CONFIG_BLOB_KEY] = ( + self._endpoint_blob_state + ) + elif self.state.blobset and self.state.blobset.blobs: + self.state.blobset.blobs.pop(IOT_ENDPOINT_CONFIG_BLOB_KEY, None) + if not self.state.blobset.blobs: + self.state.blobset = None should_block = bypass_throttle if blocking is None else blocking self.dispatcher.publish_state(self.state, wait=should_block) self._loop_state.last_state_publish_time = time.time() diff --git a/clientlib/python/src/udmi/core/managers/base_manager.py b/clientlib/python/src/udmi/core/managers/base_manager.py index 7ece646c0e..46d1982008 100644 --- a/clientlib/python/src/udmi/core/managers/base_manager.py +++ b/clientlib/python/src/udmi/core/managers/base_manager.py @@ -102,6 +102,24 @@ def publish_event(self, event_model: DataModel, subfolder: str, self._dispatcher.publish_event(f"events/{subfolder}", event_model, device_id) + def publish_upload(self, upload_model: DataModel, subfolder: str, + device_id: Optional[str] = None) -> None: + """ + Helper method for managers to publish upload stream messages. + + Args: + upload_model: The UDMI data model to publish. + subfolder: The upload subfolder (e.g., 'discovery'). + device_id: (Optional) The device ID to publish for. + """ + if not self._dispatcher: + LOGGER.error("Manager %s cannot publish upload: dispatcher not set.", + self.__class__.__name__) + return + + self._dispatcher.publish_event(f"upload/{subfolder}", upload_model, + device_id) + def trigger_state_update(self, immediate: bool = False) -> None: """ Requests the device to publish its state. @@ -196,6 +214,12 @@ def handle_command(self, command_name: str, payload: dict) -> None: """ raise NotImplementedError + def handle_download(self, download_name: str, payload: dict) -> None: + """ + Handle a newly received download message. + Optional to implement. + """ + @abc.abstractmethod def update_state(self, state: State) -> None: """ diff --git a/clientlib/python/src/udmi/core/managers/gateway_manager.py b/clientlib/python/src/udmi/core/managers/gateway_manager.py index 96b690338d..4b15adf894 100644 --- a/clientlib/python/src/udmi/core/managers/gateway_manager.py +++ b/clientlib/python/src/udmi/core/managers/gateway_manager.py @@ -31,6 +31,8 @@ ProxyConfigHandler = Callable[[str, Config], None] # ProxyCommandHandler: (device_id, command_name, payload) -> None ProxyCommandHandler = Callable[[str, str, dict], None] +# ProxyDownloadHandler: (device_id, download_name, payload) -> None +ProxyDownloadHandler = Callable[[str, str, dict], None] class GatewayManager(BaseManager): @@ -39,6 +41,8 @@ class GatewayManager(BaseManager): Allows this device to act as a gateway for other 'proxy' devices. """ + # pylint: disable=too-many-instance-attributes + @property def model_field_name(self) -> str: return "gateway" @@ -56,8 +60,10 @@ def __init__(self) -> None: self._proxies: List[str] = [] self._proxy_config_handlers: Dict[str, ProxyConfigHandler] = {} self._proxy_command_handlers: Dict[str, ProxyCommandHandler] = {} + self._proxy_download_handlers: Dict[str, ProxyDownloadHandler] = {} self._default_config_handler: Optional[ProxyConfigHandler] = None self._default_command_handler: Optional[ProxyCommandHandler] = None + self._default_download_handler: Optional[ProxyDownloadHandler] = None self._gateway_state = GatewayState() LOGGER.info("GatewayManager initialized.") @@ -168,9 +174,13 @@ def update_state(self, state: State) -> None: # --- Public API --- - def add_proxy(self, device_id: str, - config_handler: Optional[ProxyConfigHandler] = None, - command_handler: Optional[ProxyCommandHandler] = None) -> None: + def add_proxy( + self, + device_id: str, + config_handler: Optional[ProxyConfigHandler] = None, + command_handler: Optional[ProxyCommandHandler] = None, + download_handler: Optional[ProxyDownloadHandler] = None, + ) -> None: """ Registers a proxy device, persists it, and sends the 'attach' message. @@ -178,6 +188,7 @@ def add_proxy(self, device_id: str, device_id: The unique ID of the proxy device. config_handler: Optional callback for this proxy's config updates. command_handler: Optional callback for this proxy's commands. + download_handler: Optional callback for this proxy's downloads. """ if device_id not in self._proxies: self._proxies.append(device_id) @@ -189,11 +200,15 @@ def add_proxy(self, device_id: str, device_id) self._dispatcher.client.register_channel_subscription("commands/#", device_id) + self._dispatcher.client.register_channel_subscription("download/#", + device_id) if config_handler: self._proxy_config_handlers[device_id] = config_handler if command_handler: self._proxy_command_handlers[device_id] = command_handler + if download_handler: + self._proxy_download_handlers[device_id] = download_handler def remove_proxy(self, device_id: str) -> None: """ @@ -206,6 +221,7 @@ def remove_proxy(self, device_id: str) -> None: self._proxies.remove(device_id) self._proxy_config_handlers.pop(device_id, None) self._proxy_command_handlers.pop(device_id, None) + self._proxy_download_handlers.pop(device_id, None) LOGGER.info("Detached proxy device: %s", device_id) self._save_proxies() if self._dispatcher: @@ -249,6 +265,28 @@ def handle_proxy_command(self, device_id: str, command_name: str, payload: dict) except Exception as e: # pylint: disable=broad-exception-caught LOGGER.error("Error in proxy command handler for %s: %s", device_id, e) + def handle_proxy_download( + self, device_id: str, download_name: str, payload: dict + ) -> None: + """ + Routes downloads to the specific proxy handler. + + Args: + device_id: The ID of the proxy device receiving the download. + download_name: The name of the download channel (e.g., 'blobset'). + payload: The download payload dictionary. + """ + handler = self._proxy_download_handlers.get( + device_id, self._default_download_handler + ) + if handler: + try: + handler(device_id, download_name, payload) + except Exception as e: # pylint: disable=broad-exception-caught + LOGGER.error( + "Error in proxy download handler for %s: %s", device_id, e + ) + def _send_attach_message(self, device_id: str) -> None: """ Publishes an MQTT 'attach' message to the broker for the given device. @@ -292,14 +330,20 @@ def publish_proxy_event(self, device_id: str, event_model: DataModel, subfolder: if self._dispatcher: self._dispatcher.publish_event(f"events/{subfolder}", event_model, device_id=device_id) - def set_default_handlers(self, config_handler: Optional[ProxyConfigHandler] = None, - command_handler: Optional[ProxyCommandHandler] = None) -> None: + def set_default_handlers( + self, + config_handler: Optional[ProxyConfigHandler] = None, + command_handler: Optional[ProxyCommandHandler] = None, + download_handler: Optional[ProxyDownloadHandler] = None, + ) -> None: """ Sets the default handlers for proxies that don't have specific ones registered. Args: config_handler: The default callback for config updates. command_handler: The default callback for commands. + download_handler: The default callback for downloads. """ self._default_config_handler = config_handler self._default_command_handler = command_handler + self._default_download_handler = download_handler diff --git a/clientlib/python/src/udmi/core/managers/system_manager.py b/clientlib/python/src/udmi/core/managers/system_manager.py index 7941888b63..ef5f0fa24d 100644 --- a/clientlib/python/src/udmi/core/managers/system_manager.py +++ b/clientlib/python/src/udmi/core/managers/system_manager.py @@ -516,7 +516,7 @@ def update_state(self, state: State) -> None: state.system = self._system_state if self._blob_states: - state.blobset = BlobsetState(blobs=self._blob_states) + state.blobset = BlobsetState(blobs=dict(self._blob_states)) def _publish_startup_event(self) -> None: """Publishes the system startup event log.""" diff --git a/clientlib/python/src/udmi/core/messaging/mqtt_messaging_client.py b/clientlib/python/src/udmi/core/messaging/mqtt_messaging_client.py index 2995cd3918..a51854ed5d 100644 --- a/clientlib/python/src/udmi/core/messaging/mqtt_messaging_client.py +++ b/clientlib/python/src/udmi/core/messaging/mqtt_messaging_client.py @@ -196,8 +196,9 @@ def register_channel_subscription(self, channel: str, topic = "/" + topic LOGGER.info("Dynamic subscription to: %s", topic) + qos = 0 if channel.startswith("download") else 1 try: - self._mqtt_client.subscribe(topic, qos=1) + self._mqtt_client.subscribe(topic, qos=qos) except Exception as e: # pylint: disable=broad-exception-caught LOGGER.error("Subscribe failed for %s: %s", topic, e) @@ -260,8 +261,9 @@ def _on_connect(self, client: Any, _userdata: Any, _flags: Any, topic = f"{self._topic_prefix_str}/{device_id}/{channel}" if not topic.startswith("/"): topic = "/" + topic + qos = 0 if channel.startswith("download") else 1 try: - client.subscribe(topic, qos=1) + client.subscribe(topic, qos=qos) except Exception as e: # pylint: disable=broad-exception-caught LOGGER.error("Subscribe failed for %s: %s", topic, e) diff --git a/clientlib/python/tests/core/managers/test_gateway_manager.py b/clientlib/python/tests/core/managers/test_gateway_manager.py index f39626d33b..609efa5ed1 100644 --- a/clientlib/python/tests/core/managers/test_gateway_manager.py +++ b/clientlib/python/tests/core/managers/test_gateway_manager.py @@ -94,7 +94,8 @@ def test_add_proxy_flow(manager, mock_device, mock_dispatcher): expected_subs = [ call("config", "new-proxy"), - call("commands/#", "new-proxy") + call("commands/#", "new-proxy"), + call("download/#", "new-proxy"), ] client.register_channel_subscription.assert_has_calls(expected_subs, any_order=True) @@ -191,10 +192,14 @@ def test_proxy_message_routing(manager): """ mock_config_handler = MagicMock() mock_command_handler = MagicMock() + mock_download_handler = MagicMock() - manager.add_proxy("proxy-1", - config_handler=mock_config_handler, - command_handler=mock_command_handler) + manager.add_proxy( + "proxy-1", + config_handler=mock_config_handler, + command_handler=mock_command_handler, + download_handler=mock_download_handler, + ) config_obj = Config(timestamp="now") manager.handle_proxy_config("proxy-1", config_obj) @@ -204,6 +209,12 @@ def test_proxy_message_routing(manager): manager.handle_proxy_command("proxy-1", "reset", payload) mock_command_handler.assert_called_once_with("proxy-1", "reset", payload) + download_payload = {"blobs": {}} + manager.handle_proxy_download("proxy-1", "blobset", download_payload) + mock_download_handler.assert_called_once_with( + "proxy-1", "blobset", download_payload + ) + def test_proxy_routing_exception_safety(manager, caplog): """ diff --git a/clientlib/python/tests/core/messaging/test_mqtt_messaging_client.py b/clientlib/python/tests/core/messaging/test_mqtt_messaging_client.py index 2919e98537..913484637e 100644 --- a/clientlib/python/tests/core/messaging/test_mqtt_messaging_client.py +++ b/clientlib/python/tests/core/messaging/test_mqtt_messaging_client.py @@ -192,16 +192,18 @@ def test_on_connect_subscribes_to_channels(mqtt_client, """ test_on_connect_subscribes_to_channels Verify mock_paho_client_instance.subscribe was called with - the full topic. + the full topic and appropriate QoS (0 for download, 1 otherwise). """ mqtt_client.register_channel_subscription("config") mqtt_client.register_channel_subscription("commands/#") + mqtt_client.register_channel_subscription("download/#") mqtt_client._on_connect(mock_paho_client_instance, None, None, 0) expected_calls = [ call("/devices/d/config", qos=1), - call("/devices/d/commands/#", qos=1) + call("/devices/d/commands/#", qos=1), + call("/devices/d/download/#", qos=0), ] mock_paho_client_instance.subscribe.assert_has_calls( expected_calls, diff --git a/clientlib/python/tests/core/test_device.py b/clientlib/python/tests/core/test_device.py index b4e9c81f8b..e546238177 100644 --- a/clientlib/python/tests/core/test_device.py +++ b/clientlib/python/tests/core/test_device.py @@ -22,6 +22,8 @@ invalid config is not passed to managers. """ +import hashlib +import json import logging import time from unittest.mock import MagicMock @@ -233,6 +235,269 @@ def test_redirection_trigger(mock_parse_blob, test_device): assert test_device._loop_state.reset_event.is_set() +def test_download_channel_redirection_flow(test_device, mock_dispatcher): + """ + Verifies that a download:// endpoint blob URL transitions state to 'apply' + and then applies the EndpointConfiguration delivered on 'download/blobset'. + """ + mock_persistence = MagicMock() + test_device.persistence = mock_persistence + mock_persistence.get_active_generation.return_value = "old-gen" + + endpoint_blob_dict = { + "protocol": "mqtt", + "hostname": "new.broker.local", + "port": 8883, + "client_id": "projects/p/locations/l/registries/r/devices/d", + } + + valid_sha = hashlib.sha256( + json.dumps(endpoint_blob_dict, sort_keys=True, separators=(",", ":")).encode("utf-8") + ).hexdigest() + + blob_config = BlobBlobsetConfig( + generation="new-gen", + url="download://blobset/_iot_endpoint_config", + sha256=valid_sha, + ) + config = Config( + blobset=BlobsetConfig( + blobs={IOT_ENDPOINT_CONFIG_BLOB_KEY: blob_config} + ) + ) + + with patch("src.udmi.core.device.STATE_THROTTLE_SEC", 0): + test_device.handle_config( + test_device.device_id, "config", config.to_dict() + ) + + assert not test_device._loop_state.reset_event.is_set() + assert ( + test_device.state.blobset.blobs[IOT_ENDPOINT_CONFIG_BLOB_KEY].phase.value + == "apply" + ) + mock_dispatcher.publish_state.assert_called_once() + + download_payload = { + "timestamp": "2026-03-31T10:00:00Z", + "version": "1.5.7", + "blobs": { + IOT_ENDPOINT_CONFIG_BLOB_KEY: endpoint_blob_dict, + }, + } + test_device.handle_download( + test_device.device_id, "download/blobset", download_payload + ) + + mock_persistence.save_active_endpoint.assert_called_once() + saved_endpoint, saved_gen = mock_persistence.save_active_endpoint.call_args[0] + assert saved_endpoint.hostname == "new.broker.local" + assert saved_gen == "new-gen" + assert ( + test_device.state.blobset.blobs[IOT_ENDPOINT_CONFIG_BLOB_KEY].phase.value + == "final" + ) + assert ( + test_device.state.blobset.blobs[IOT_ENDPOINT_CONFIG_BLOB_KEY].status + is None + ) + assert test_device._loop_state.reset_event.is_set() + + +def test_download_channel_bad_sha256(test_device): + """ + Verifies that a downloaded endpoint blob with a mismatched SHA-256 hash + is rejected and transitions state to 'final' with a level 500 error status. + """ + mock_persistence = MagicMock() + test_device.persistence = mock_persistence + mock_persistence.get_active_generation.return_value = "old-gen" + + blob_config = BlobBlobsetConfig( + generation="new-gen", + url="download://blobset/_iot_endpoint_config", + sha256="0" * 64, + ) + config = Config( + blobset=BlobsetConfig( + blobs={IOT_ENDPOINT_CONFIG_BLOB_KEY: blob_config} + ) + ) + + with patch("src.udmi.core.device.STATE_THROTTLE_SEC", 0): + test_device.handle_config( + test_device.device_id, "config", config.to_dict() + ) + + download_payload = { + "timestamp": "2026-03-31T10:00:00Z", + "version": "1.5.7", + "blobs": { + IOT_ENDPOINT_CONFIG_BLOB_KEY: { + "protocol": "mqtt", + "hostname": "new.broker.local", + "port": 8883, + "client_id": "projects/p/locations/l/registries/r/devices/d", + } + }, + } + test_device.handle_download( + test_device.device_id, "download/blobset", download_payload + ) + + mock_persistence.save_active_endpoint.assert_not_called() + assert not test_device._loop_state.reset_event.is_set() + blob_state = test_device.state.blobset.blobs[IOT_ENDPOINT_CONFIG_BLOB_KEY] + assert blob_state.phase.value == "final" + assert blob_state.status is not None + assert blob_state.status.level == 500 + assert blob_state.status.category == "blobset.blob.apply" + + +def test_download_channel_expired_endpoint(test_device): + """ + Verifies that an already-expired downloaded endpoint configuration is + rejected and transitions state to 'final' with a level 500 error status. + """ + + mock_persistence = MagicMock() + test_device.persistence = mock_persistence + mock_persistence.get_active_generation.return_value = "old-gen" + + expired_blob = { + "protocol": "mqtt", + "hostname": "new.broker.local", + "port": 8883, + "client_id": "projects/p/locations/l/registries/r/devices/d", + "expiry": "2020-01-01T00:00:00Z", + } + valid_sha = hashlib.sha256( + json.dumps( + expired_blob, sort_keys=True, separators=(",", ":") + ).encode("utf-8") + ).hexdigest() + + blob_config = BlobBlobsetConfig( + generation="new-gen", + url="download://blobset/_iot_endpoint_config", + sha256=valid_sha, + ) + config = Config( + blobset=BlobsetConfig( + blobs={IOT_ENDPOINT_CONFIG_BLOB_KEY: blob_config} + ) + ) + + with patch("src.udmi.core.device.STATE_THROTTLE_SEC", 0): + test_device.handle_config( + test_device.device_id, "config", config.to_dict() + ) + + download_payload = { + "timestamp": "2026-03-31T10:00:00Z", + "version": "1.5.7", + "blobs": { + IOT_ENDPOINT_CONFIG_BLOB_KEY: expired_blob, + }, + } + test_device.handle_download( + test_device.device_id, "download/blobset", download_payload + ) + + mock_persistence.save_active_endpoint.assert_not_called() + assert not test_device._loop_state.reset_event.is_set() + blob_state = test_device.state.blobset.blobs[IOT_ENDPOINT_CONFIG_BLOB_KEY] + assert blob_state.phase.value == "final" + assert blob_state.status is not None + assert blob_state.status.level == 500 + assert blob_state.status.category == "blobset.blob.apply" + + # Clearing config.blobset must remove _iot_endpoint_config from state.blobset + with patch("src.udmi.core.device.STATE_THROTTLE_SEC", 0): + test_device.handle_config( + test_device.device_id, "config", Config().to_dict() + ) + assert test_device.state.blobset is None + + +def test_download_channel_unsolicited_ignored(test_device): + """ + Verifies that an unsolicited download/blobset message arriving when no + download://blobset/_iot_endpoint_config is pending is ignored. + """ + mock_persistence = MagicMock() + test_device.persistence = mock_persistence + + download_payload = { + "timestamp": "2026-03-31T10:00:00Z", + "version": "1.5.7", + "blobs": { + IOT_ENDPOINT_CONFIG_BLOB_KEY: { + "protocol": "mqtt", + "hostname": "unsolicited.broker.local", + "port": 8883, + "client_id": "projects/p/locations/l/registries/r/devices/d", + } + }, + } + test_device.handle_download( + test_device.device_id, "download/blobset", download_payload + ) + + mock_persistence.save_active_endpoint.assert_not_called() + assert not test_device._loop_state.reset_event.is_set() + assert test_device.state.blobset is None + + +def test_download_channel_missing_sha256(test_device): + """ + Verifies that a downloaded endpoint blob when config.blobset has no + expected sha256 is rejected with level 500 error status. + """ + mock_persistence = MagicMock() + test_device.persistence = mock_persistence + mock_persistence.get_active_generation.return_value = "old-gen" + + blob_config = BlobBlobsetConfig( + generation="new-gen", + url="download://blobset/_iot_endpoint_config", + ) + config = Config( + blobset=BlobsetConfig( + blobs={IOT_ENDPOINT_CONFIG_BLOB_KEY: blob_config} + ) + ) + + with patch("src.udmi.core.device.STATE_THROTTLE_SEC", 0): + test_device.handle_config( + test_device.device_id, "config", config.to_dict() + ) + + download_payload = { + "timestamp": "2026-03-31T10:00:00Z", + "version": "1.5.7", + "blobs": { + IOT_ENDPOINT_CONFIG_BLOB_KEY: { + "protocol": "mqtt", + "hostname": "new.broker.local", + "port": 8883, + "client_id": "projects/p/locations/l/registries/r/devices/d", + } + }, + } + test_device.handle_download( + test_device.device_id, "download/blobset", download_payload + ) + + mock_persistence.save_active_endpoint.assert_not_called() + assert not test_device._loop_state.reset_event.is_set() + blob_state = test_device.state.blobset.blobs[IOT_ENDPOINT_CONFIG_BLOB_KEY] + assert blob_state.phase.value == "final" + assert blob_state.status is not None + assert blob_state.status.level == 500 + assert blob_state.status.category == "blobset.blob.apply" + + # --- Resilience / Fallback Tests --- def test_connection_fallback_clears_active_endpoint(test_device): diff --git a/docs/messages/readme.md b/docs/messages/readme.md index f1d2bf0411..67826ae896 100644 --- a/docs/messages/readme.md +++ b/docs/messages/readme.md @@ -34,14 +34,17 @@ flow, but also the treatment of the message as it moves through the system: |---------|---------|-----------| | __Sticky__ | _state_ | _config_ | | __Stream__ | _events_ | _command_ | +| __Out-of-Band / Ephemeral__ | _upload_ | _download_ | -The properties and uses of the four types fall out from this accordingly: +The properties and uses of the message types fall out from this accordingly: * _state_: Sticky to the cloud information from the device, including information such as the overall status of the device, and any errors or conditions from, e.g., writing points. * _events_: The canonical streaming telemetry messages from the device, usually containing things like temperature readings or system memory utilization. * _config_: Ability to control the behavior of a device, e.g. for key rotation, writeback, etc... * _command_: Direct but transitory messages to a device, e.g. to install a new auth key or perform diagnostic operations. +* _upload_: Chunked bulk binary or text artifact transfers from the device to the cloud (e.g., `upload/discovery` for packet captures), framed by `config` and `state`. +* _download_: Ephemeral, non-retained (`QoS 0`, `retain = false`) payload delivery from the cloud to the device (e.g., `download/blobset` for credentials and private keys), framed by `config.blobset` and `state.blobset`. Individual blocks (folders), such as _system_ or _pointset_ will have their own semantic uses for the various types. See the individual block folder documentation to learn more about those aspects. @@ -55,6 +58,10 @@ See the individual block folder documentation to learn more about those aspects. - [Alarmset (telemetry)](alarmset.md#telemetry) ([_🧬schema_](../../gencode/docs/events_alarmset.html)) - [System (logging, etc)](system.md) ([_🧬schema_](../../gencode/docs/events_system.html)) - [Discovery](../specs/discovery.md) ([_🧬schema_](../../gencode/docs/events_discovery.html)) +- upload + - [Discovery Upload](../specs/discovery.md#discovery-uploads) ([_🧬schema_](../../gencode/docs/upload_discovery.html)) +- download + - [Blobset Download](../specs/blob_updates.md#ephemeral-blobset-downloads) ([_🧬schema_](../../gencode/docs/download_blobset.html)) - [envelope](envelope.md) ## MQTT Topics diff --git a/docs/specs/blob_updates.md b/docs/specs/blob_updates.md index 2ac15d8c5d..3637a7121d 100644 --- a/docs/specs/blob_updates.md +++ b/docs/specs/blob_updates.md @@ -137,3 +137,38 @@ Granular observability relies on consistent telemetry. When tracking the lifecyc } } ``` + +--- + +## Ephemeral Blobset Downloads (`download/blobset`) + +When delivering sensitive credentials or ephemeral tokens (`_iot_endpoint_config`), embedding secret material inline in `config.blobset` (via `data:` URLs) is undesirable because `config` messages are retained by the MQTT broker and stored in operational databases. + +Instead, the cloud can specify a `download://` URL (e.g., `download://_iot_endpoint_config`) in `config.blobset.blobs..url` and deliver the sensitive payload out-of-band over the `{topic_prefix}/download/blobset` MQTT topic ([`schema/download_blobset.json`](../../schema/download_blobset.json)). + +### Transport & Security Guarantees +* **QoS 0 & Non-Retained**: Both the publisher and device subscriber **must** use **MQTT QoS 0** with `retain = false` on `{topic_prefix}/download/blobset` so the broker never persists unacknowledged secret payloads to durable session disk. +* **Direct Broker Delivery**: UDMIS pushes `download/blobset` messages directly to the MQTT broker without routing the secret payload through intermediate Pub/Sub topics. + +### System Blobset Keys (`schema/common.json#/definitions/blobsets`) +Standardized UDMI system blob keys use a leading underscore: +* `_iot_endpoint_config`: Primary MQTT broker endpoint configuration and credentials (including automated key/password rotation). + +### `EndpointConfiguration` Extensions (`schema/configuration_endpoint.json`) +Each entry in `download/blobset` (`blobs.`) conforms to [`EndpointConfiguration`](../../schema/configuration_endpoint.json), which supports: +* **`expiry`** (`date-time`): Optional RFC 3339 UTC timestamp indicating when an ephemeral credential expires. Once `now >= expiry`, the device must purge the credential from memory and, if the blob is still active in `config.blobset`, transition `state.blobset.blobs..phase` back to `apply` to request a refreshed credential. +* **`auth_provider.basic`**: `username` and `password`. +* **`auth_provider.jwt`**: `audience`. + +### Handshake & Crash Recovery Flow +1. **Config Notification**: Cloud publishes `config.blobset.blobs.` with `phase: "final"`, `generation`, `sha256` of the serialized `EndpointConfiguration`, and `url: "download://"`. +2. **Download Request (`phase: "apply"`)**: Seeing a new `generation` with a `download://` URL (or recovering after a reboot/crash where the ephemeral secret is no longer in memory), the device publishes `state.blobset.blobs..phase = "apply"` with the matching `generation`. +3. **Ephemeral Delivery**: Upon receiving `state.blobset.blobs..phase == "apply"`, UDMIS publishes a [`BlobsetDownload`](../../schema/download_blobset.json) message to `{topic_prefix}/download/blobset` containing `blobs.`. +4. **Application & Acknowledgment (`phase: "final"`)**: The device verifies that the SHA-256 digest of the received payload matches `config.blobset.blobs..sha256`, applies the `EndpointConfiguration`, and publishes `state.blobset.blobs..phase = "final"`. + +### Coexistence with Inline `data:` URLs +To maintain backward compatibility with existing devices and test sequences: +* **`data:` URLs**: Non-secret endpoint redirection updates (such as updating `hostname`, `port`, or `client_id` while reusing an existing on-device private key) continue to use `data:application/json;base64,...` inline in `config.blobset.blobs._iot_endpoint_config.url`. Clients decode and apply `data:` URLs immediately upon receiving `config`. +* **`download://` URLs**: Secret-bearing blobs (`basic.password`, `_iot_endpoint_config`) use `download://...`, triggering the `state` (`phase: "apply"`) → `download/blobset` → `state` (`phase: "final"`) flow. + + diff --git a/docs/specs/discovery.md b/docs/specs/discovery.md index fd2f9c28ea..666a973806 100644 --- a/docs/specs/discovery.md +++ b/docs/specs/discovery.md @@ -79,3 +79,16 @@ be reported: * `blobs`: A listing of all the data blobs that a device knows how to handle. This could be components like firmware updates, key rotation, etc... Some blobs will be standardized across the system, while others will be device-specific. + +## Discovery Uploads (`upload/discovery`) + +When a discovery scan produces bulk binary or text capture artifacts—such as network packet captures (`pcap` / `pcapng`) triggered via `config.discovery.families.`—the device streams the capture in chunks over `{topic_prefix}/upload/discovery` using the [`DiscoveryUpload`](../../schema/upload_discovery.json) ([_🧬schema_](../../gencode/docs/upload_discovery.html)) format rather than sending individual telemetry events. + +### Chunk Framing & Sequence (`event_no`) +Each `upload/discovery` message is correlated with the active scan via `(family, generation)` and sequenced using `event_no`: +* **`event_no = 0` (Start Marker)**: Announces the start of the upload stream (`family`, `generation`, `event_no = 0`). +* **`event_no = 1 .. N` (Data Chunks)**: Sequential 1-based chunks carrying Base64-encoded binary data in `data`. +* **`event_no = -(N + 1)` (Terminal End-of-File Marker)**: Mandatory final message where the negative value indicates the total chunk count `N` (`N = (-event_no) - 1`) and `sha256` provides the 64-character lowercase hex SHA-256 digest of the complete reassembled artifact. + +In UDMIS, `UploadProcessor` stages incoming chunks in shared storage keyed by `(family, generation)` so out-of-order chunks distributed across multiple stateless pods are reassembled and cryptographically verified against `sha256` once the start marker (`event_no = 0`), all `1..N` chunks, and the terminal `-(N + 1)` marker have arrived. + diff --git a/docs/specs/tech_stack.md b/docs/specs/tech_stack.md index 3ed6adab3c..7e50a29d43 100644 --- a/docs/specs/tech_stack.md +++ b/docs/specs/tech_stack.md @@ -16,12 +16,14 @@ technology stack for compliant IoT devices. # MQTT Topic Suffix Table -| Type | Category | subFolder | MQTT Topic Suffix | Schema File | -| -------- | -------- | --------- | -------------------------------- | ------------- | -| state | state | _n/a_ | `{topic_prefix}/state` | state.json | -| config | config | _n/a_ | `{topic_prefix}/config` | config.json | -| pointset | event | pointset | `{topic_prefix}/events/pointset` | pointset.json | -| system | event | system | `{topic_prefix}/events/system` | system.json | +| Type | Category | subFolder | MQTT Topic Suffix | Schema File | +| --------- | -------- | --------- | --------------------------------- | --------------------- | +| state | state | _n/a_ | `{topic_prefix}/state` | state.json | +| config | config | _n/a_ | `{topic_prefix}/config` | config.json | +| pointset | event | pointset | `{topic_prefix}/events/pointset` | events_pointset.json | +| system | event | system | `{topic_prefix}/events/system` | events_system.json | +| discovery | upload | discovery | `{topic_prefix}/upload/discovery` | upload_discovery.json | +| blobset | download | blobset | `{topic_prefix}/download/blobset` | download_blobset.json | For many implementations the full topic would be `/devices/{device_id}/{suffix}` diff --git a/docs/tools/spotter.md b/docs/tools/spotter.md index 0d8c274160..201d8503bf 100644 --- a/docs/tools/spotter.md +++ b/docs/tools/spotter.md @@ -16,7 +16,7 @@ Spotter runs as a single unified process using the UDMI Python Client Library (` - **`PassiveFamilyProvider`**: Listens for passive broadcast network traffic and extracts discovered device metadata. 2. **`SpotterDiscoveryManager`**: - Manages scheduled and on-demand discovery sweeps. - - Streams live remote packet capture traces (`events/streams`) buffered in volatile memory with circuit-breaker protection (zero-disk streaming). + - Streams live remote packet capture traces (`upload/discovery`) buffered in volatile memory with circuit-breaker protection (zero-disk streaming). 3. **`SpotterSystemManager`**: - Collects host metrics (CPU load, memory, OS distribution) and emits periodic telemetry (`events/system`). - Evaluates memory usage against safety thresholds (`check_safety_circuit_breaker`), throttling active discovery and packet captures to protect edge devices from kernel OOM termination. @@ -48,7 +48,7 @@ graph TD end SYS -->|"state.system / events/system"| MB - DISC -->|"events/discovery & events/streams"| MB + DISC -->|"events/discovery & upload/discovery"| MB LOC -->|"Scans & Probes"| DEV ``` @@ -60,17 +60,18 @@ Spotter processes diagnostic packet capture triggers sent declaratively over the 1. **Capture Worker ([pcap.py](../../edge/spotter/src/pcap.py))**: Spawns `tcpdump` with configurable interface filters, enforcing strict execution bounds (maximum duration and byte quotas). 2. **Streaming MQTT Egress Transport**: - - **Zero-Disk Streaming**: Packets are buffered dynamically in volatile memory (RAM) and sequentially published as reliable base64 chunks (`StreamsEvents`) over the universal streaming MQTT event topic (`events/streams`). + - **Zero-Disk Streaming**: Packets are buffered dynamically in volatile memory (RAM) and sequentially published as reliable base64 chunks (`DiscoveryUpload`) over the streaming MQTT upload topic (`upload/discovery`), terminated by a SHA-256 end-of-file marker. - **Zero Secret Distribution**: Leverages the existing mTLS hardware key/certificate connection directly, avoiding external network credentials or outbound HTTP rules at the edge. -3. **Ad-hoc PCAP Reassembly ([bin/reassemble_pcap](../../edge/spotter/bin/reassemble_pcap))**: - Reassembles chunked `events/streams` messages (from JSON, JSONL, or `mosquitto_sub`) back into a valid `.pcap` binary capture file: - ```bash - # Reassemble stream events file into a pcap file: - ./edge/spotter/bin/reassemble_pcap stream_events.json capture.pcap +3. **Ad-hoc Upload Reassembly ([bin/mapper](../../bin/mapper))**: + Reassembles chunked `upload/discovery` messages (from JSON, JSONL, or `mosquitto_sub`) back into a valid `.pcap` binary capture file: - # Or stream directly from mosquitto subscriber: - mosquitto_sub -h $BROKER -t '/r/+/d/+/events/streams' | ./edge/spotter/bin/reassemble_pcap - live.pcap - ``` +```bash +# Reassemble upload chunks file into a pcap file: +bin/mapper sites/udmi_site_model //mqtt/localhost AHU-1 reassemble stream_events.json capture.pcap + +# Or stream directly from mosquitto subscriber: +mosquitto_sub -h $BROKER -t '/r/+/d/+/upload/discovery' | bin/mapper sites/udmi_site_model //mqtt/localhost AHU-1 reassemble - live.pcap +``` --- @@ -133,7 +134,7 @@ Spotter includes a comprehensive suite of unit and integration tests located in ### Key Integration Tests - **`test_container`**: Validates container lifecycle isolation, volume mounting of runtime configs, clean signal termination, and exit code propagation inside Docker. -- **`test_pcap`**: Validates remote-triggered PCAP packet capture diagnostics over MQTT streaming and verifies binary reassembly via `reassemble_pcap`. +- **`test_pcap`**: Validates remote-triggered PCAP packet capture diagnostics over MQTT streaming and verifies binary reassembly via `bin/mapper`. - **`test_resource_contention`**: Validates process CPU, memory consumption, `/proc/PID/fd` file descriptor stability under load, and memory safety circuit breaker tripping. - **`test_fault_injection`**: Validates network fault tolerance, streaming MQTT backoff recovery, and socket reconnect logic under simulated broker resets. - **`test_parity`**: Runs co-existence integration testing against a simulated BACnet device on an isolated bridge network, confirming functional discovery parity with legacy discovery nodes. diff --git a/edge/spotter/GEMINI.md b/edge/spotter/GEMINI.md index e43c7fc5d8..1e13e1a1b0 100644 --- a/edge/spotter/GEMINI.md +++ b/edge/spotter/GEMINI.md @@ -31,7 +31,7 @@ All verification tests are categorised into **Unit Tests** and **Integration Tes - **Location**: [bin/](bin) & [tests/](tests) - **Executables**: - [test_container](bin/test_container): Validates container lifecycle isolation, volume mounting of configuration files, clean signal termination, and exit code propagation inside Docker. - - [test_pcap](bin/test_pcap): Validates remote-triggered PCAP packet capture diagnostics over MQTT streaming and verifies binary reassembly via [reassemble_pcap](bin/reassemble_pcap). + - [test_pcap](bin/test_pcap): Validates remote-triggered PCAP packet capture diagnostics over MQTT streaming and verifies binary reassembly via [bin/mapper](../../bin/mapper). - [test_parity](bin/test_parity): Runs co-existence integration testing against a simulated BACnet device on an isolated network, confirming functional parity with legacy discovery nodes. - [compare_field_parity](bin/compare_field_parity): Automated utility to run sequential discovery parity scans or diff event logs on live field testbeds. - [test_resource_contention](bin/test_resource_contention): Validates Spotter process CPU, memory consumption, file descriptor limits, and telemetry latency under concurrent heavy workloads. diff --git a/edge/spotter/README.md b/edge/spotter/README.md index 78bb4cbd79..8c98e0d4ed 100644 --- a/edge/spotter/README.md +++ b/edge/spotter/README.md @@ -14,7 +14,7 @@ Spotter runs as a single unified process using the UDMI Python Client Library (` - **`PassiveFamilyProvider`**: Listens for passive broadcast network traffic and extracts discovered device metadata. 2. **`SpotterDiscoveryManager`**: - Manages scheduled and on-demand discovery sweeps. - - Streams live remote packet capture traces (`events/streams`) buffered in volatile memory with circuit-breaker protection (zero-disk streaming). + - Streams live remote packet capture traces (`upload/discovery`) buffered in volatile memory with circuit-breaker protection (zero-disk streaming). 3. **`SpotterSystemManager`**: - Collects host metrics (CPU load, memory, OS distribution) and emits periodic telemetry (`events/system`). - Evaluates memory usage against safety thresholds (`check_safety_circuit_breaker`), throttling active discovery and packet captures to protect edge devices from kernel OOM termination. @@ -46,7 +46,7 @@ graph TD end SYS -->|"state.system / events/system"| MB - DISC -->|"events/discovery & events/streams"| MB + DISC -->|"events/discovery & upload/discovery"| MB LOC -->|"Scans & Probes"| DEV ``` @@ -78,16 +78,16 @@ Spotter processes diagnostic packet capture triggers sent declaratively over the 1. **Capture Worker ([pcap.py](src/pcap.py))**: Spawns `tcpdump` with configurable interface filters, enforcing strict execution bounds (maximum duration and byte quotas). 2. **Streaming MQTT Egress Transport**: - - **Zero-Disk Streaming**: Packets are buffered dynamically in volatile memory (RAM) and sequentially published as reliable base64 chunks (`StreamsEvents`) over the universal streaming MQTT event topic (`events/streams`). + - **Zero-Disk Streaming**: Packets are buffered dynamically in volatile memory (RAM) and sequentially published as reliable base64 chunks (`DiscoveryUpload`) over the streaming MQTT upload topic (`upload/discovery`), terminated by a SHA-256 EOF marker. - **Zero Secret Distribution**: Leverages the existing mTLS hardware key/certificate connection directly, avoiding external network credentials or outbound HTTP rules at the edge. -3. **Ad-hoc PCAP Reassembly ([bin/reassemble_pcap](bin/reassemble_pcap))**: - Reassembles chunked `events/streams` messages (from JSON, JSONL, or `mosquitto_sub`) back into a valid `.pcap` binary capture file: +3. **Ad-hoc Upload Reassembly ([bin/mapper](../../bin/mapper))**: + Reassembles chunked `upload/discovery` messages (from JSON, JSONL, or `mosquitto_sub`) back into a valid `.pcap` binary capture file: ```bash - # Reassemble stream events file into a pcap file: - ./edge/spotter/bin/reassemble_pcap stream_events.json capture.pcap + # Reassemble upload chunks file into a pcap file: + bin/mapper sites/udmi_site_model //mqtt/localhost DSN-1 reassemble stream_events.json capture.pcap # Or stream directly from mosquitto subscriber: - mosquitto_sub -h $BROKER -t '/r/+/d/+/events/streams' | ./edge/spotter/bin/reassemble_pcap - live.pcap + mosquitto_sub -h $BROKER -t '/r/+/d/+/upload/discovery' | bin/mapper sites/udmi_site_model //mqtt/localhost DSN-1 reassemble - live.pcap ``` --- @@ -98,7 +98,6 @@ Spotter processes diagnostic packet capture triggers sent declaratively over the | :--- | :--- | | **[bin/spotter](../../bin/spotter)** | Unified CLI orchestrator for starting (local/container) and stopping instances | | **[bin/run_spotter_tests](bin/run_spotter_tests)** | Unified verification test runner for unit and integration suites | -| **[bin/reassemble_pcap](bin/reassemble_pcap)** | Ad-hoc CLI utility to reassemble `events/streams` chunks into `.pcap` files | | **[bin/compare_field_parity](bin/compare_field_parity)** | Automated CLI tool to verify functional parity between legacy node and Spotter | | **[src/agent.py](src/agent.py)** | Main Spotter agent entry point, manager wiring, and command dispatcher | | **[src/providers/](src/providers/)** | Modular protocol discovery providers (BACnet, Ether, Passive) | @@ -165,7 +164,7 @@ Spotter includes a comprehensive suite of unit and integration tests located in ### Key Integration Tests - **`test_container`**: Validates container lifecycle isolation, volume mounting of runtime configs, clean signal termination, and exit code propagation inside Docker. -- **`test_pcap`**: Validates remote-triggered PCAP packet capture diagnostics over MQTT streaming and verifies binary reassembly via `reassemble_pcap`. +- **`test_pcap`**: Validates remote-triggered PCAP packet capture diagnostics over MQTT streaming and verifies binary reassembly via `bin/mapper`. - **`test_resource_contention`**: Validates process CPU, memory consumption, `/proc/PID/fd` file descriptor stability under load, and memory safety circuit breaker tripping. - **`test_fault_injection`**: Validates network fault tolerance, streaming MQTT backoff recovery, and socket reconnect logic under simulated broker resets. - **`test_parity`**: Runs co-existence integration testing against a simulated BACnet device on an isolated bridge network, confirming functional parity with legacy discovery nodes. @@ -189,7 +188,7 @@ sudo ./edge/spotter/bin/compare_field_parity --config /etc/udmi_discovery/config The following capabilities are tracked as future milestones: -1. **UDMIS Native PCAP Ingestion Pipeline**: Native service-side ingestion and reassembly in UDMIS to automatically collect and persist `events/streams` PCAP chunks directly into Cloud Storage (GCS) or BigQuery blob storage. -2. **Ephemeral Secret Delivery**: Architectural mechanism for delivering transient, authenticated operational credentials (e.g., vendor device credentials, BACnet network encryption keys, or REST API bearer tokens) strictly in volatile RAM without disk persistence. A dedicated architectural discussion will finalize the delivery transport (such as non-persisted command channels e.g. `commands/secret` vs encrypted `config.blobset`), memory scrubbing lifecycles, and cryptographic envelope validation before implementation. +1. **UDMIS Native PCAP Ingestion Pipeline**: Native service-side ingestion and reassembly in UDMIS (`UploadProcessor`) to automatically collect and persist `upload/discovery` PCAP chunks directly into Cloud Storage (GCS) or `IotDataProvider`. +2. **Ephemeral Secret Delivery**: Architectural mechanism for delivering transient, authenticated operational credentials (e.g., vendor device credentials, BACnet network encryption keys, or REST API bearer tokens) strictly in volatile RAM without disk persistence via `download/blobset`. 3. **Key Rotation & Lifecycle**: End-to-end device private/public key rotation with automated cloud IoT registry coordination, backup verification, and zero-downtime reconnection. 4. **Expanded Host Observability Metrics**: Network adapter error/drop counters, hardware temperature, storage/inode thresholds, and edge-to-cloud roundtrip latency. diff --git a/edge/spotter/bin/reassemble_pcap b/edge/spotter/bin/reassemble_pcap deleted file mode 100755 index a542d9b34a..0000000000 --- a/edge/spotter/bin/reassemble_pcap +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python3 -"""Utility to reassemble UDMI events/streams chunks into a valid PCAP file. - -Usage: - bin/reassemble_pcap - -Supported input formats: - - Raw JSON array of StreamsEvents: [ {"event_no": 0, "data": "..."}, ... ] - - JSON Lines (JSONL): One StreamsEvents JSON object per line - - mosquitto_sub output: " " - - Standard input (specify "-" as input file) -""" - -import argparse -import base64 -import json -import os -import sys - -PCAP_MAGIC_NUMBERS = [ - b"\xa1\xb2\xc3\xd4", # Standard pcap (same-endian) - b"\xd4\xc3\xb2\xa1", # Standard pcap (swapped-endian) - b"\xa1\xb2\x3c\x4d", # Nanosecond pcap (same-endian) - b"\x4d\x3c\xb2\xa1", # Nanosecond pcap (swapped-endian) -] - - -def parse_stream_events(raw_text: str): - """Parse StreamsEvents objects from JSON array, JSONL, or mosquitto_sub.""" - raw_text = raw_text.strip() - if not raw_text: - return [] - - # Case 1: Standard JSON array or single object - if raw_text.startswith("[") or raw_text.startswith("{"): - try: - data = json.loads(raw_text) - if isinstance(data, list): - return [item.get("payload", item) if (isinstance(item, dict) and "payload" in item and isinstance(item["payload"], dict)) else item for item in data] - if isinstance(data, dict): - if "payload" in data and isinstance(data["payload"], dict) and "data" in data["payload"]: - return [data["payload"]] - return [data] - except json.JSONDecodeError: - pass - - # Case 2: Line by line (JSONL or mosquitto_sub format: " ") - events = [] - for line in raw_text.splitlines(): - line = line.strip() - if not line: - continue - # Strip topic prefix if present - if " " in line and not line.startswith("{"): - _, _, payload = line.partition(" ") - else: - payload = line - - try: - obj = json.loads(payload) - if isinstance(obj, dict): - inner = obj.get("payload") if (isinstance(obj.get("payload"), dict) and "data" in obj["payload"]) else obj - if "data" in inner: - events.append(inner) - except json.JSONDecodeError: - continue - - return events - - -def reassemble_pcap(input_source: str, output_path: str) -> None: - """Reassembles chunked events into a binary pcap capture file.""" - if input_source == "-": - raw_content = sys.stdin.read() - else: - if not os.path.exists(input_source): - sys.exit(f"Error: Input file '{input_source}' does not exist.") - with open(input_source, "r", encoding="utf-8", errors="replace") as f: - raw_content = f.read() - - events = parse_stream_events(raw_content) - if not events: - sys.exit("Error: No valid StreamsEvents records with 'data' found in input.") - - # Filter out records missing data - valid_events = [e for e in events if e.get("data")] - if not valid_events: - sys.exit("Error: Found stream records but none contained base64 'data'.") - - # Sort primarily by chunk_index, fallback to event_no - def sort_key(e): - return ( - e.get("chunk_index", 0) - if e.get("chunk_index") is not None - else e.get("event_no", 0) - ) - - sorted_events = sorted(valid_events, key=sort_key) - session_id = sorted_events[0].get("session_id", "unknown") - total_chunks = len(sorted_events) - - # Reconstruct binary payload - pcap_data = bytearray() - for idx, evt in enumerate(sorted_events): - b64_str = evt["data"] - try: - chunk_bytes = base64.b64decode(b64_str) - pcap_data.extend(chunk_bytes) - except Exception as err: # pylint: disable=broad-exception-caught - sys.exit(f"Error: Failed to decode base64 chunk at index {idx}: {err}") - - # Validate output - if not pcap_data: - sys.exit("Error: Reassembled PCAP payload is empty (0 bytes).") - - magic = bytes(pcap_data[:4]) - is_valid_magic = any(magic == m for m in PCAP_MAGIC_NUMBERS) - - # Write to destination file - os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) - with open(output_path, "wb") as f: - f.write(pcap_data) - - magic_status = "VALID" if is_valid_magic else "WARNING: Unexpected header" - print("========================================") - print("PCAP Reassembly Completed Successfully") - print("========================================") - print(f"Session ID : {session_id}") - print(f"Chunks Decoded : {total_chunks}") - print(f"Total Bytes : {len(pcap_data)}") - print(f"Magic Bytes : {magic.hex()} ({magic_status})") - print(f"Output File : {output_path}") - - -def main(): - """CLI entry point for PCAP reassembly.""" - parser = argparse.ArgumentParser( - description="Reassemble UDMI stream events into PCAP file" - ) - parser.add_argument( - "input", - help="Input file containing stream event data (or '-' for stdin)", - ) - parser.add_argument( - "output", help="Destination file path for reassembled .pcap" - ) - args = parser.parse_args() - - reassemble_pcap(args.input, args.output) - - -if __name__ == "__main__": - main() - diff --git a/edge/spotter/bin/test_fault_injection b/edge/spotter/bin/test_fault_injection index 10b36f9dca..3403d3932a 100755 --- a/edge/spotter/bin/test_fault_injection +++ b/edge/spotter/bin/test_fault_injection @@ -13,7 +13,7 @@ fi echo "=== Spotter Network Resilience & Fault Injection Test Suite ===" -echo "--- Step 1: Verifying StreamsEvents transport structure and sequence ordering ---" +echo "--- Step 1: Verifying DiscoveryUpload transport structure and sequence ordering ---" ( cd "${WORKSPACE_DIR}" PYTHONPATH=clientlib/python/src:gencode/python:. "${WORKSPACE_DIR}/edge/spotter/venv/bin/python3" -m unittest edge.spotter.tests.test_agent.TestSpotterDiscoveryManager -v diff --git a/edge/spotter/bin/test_pcap b/edge/spotter/bin/test_pcap index db43346f6a..7456ac3d11 100755 --- a/edge/spotter/bin/test_pcap +++ b/edge/spotter/bin/test_pcap @@ -10,7 +10,7 @@ if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then echo "Validates remote-triggered PCAP packet capture diagnostics over MQTT streaming:" echo "1. Starts Mosquitto broker and udmi-spotter container." echo "2. Generates dummy traffic and publishes a Discovery TRACE configuration (depth: 'trace')." - echo "3. Asserts sequential StreamsEvents chunks over events/streams and parses PCAP magic headers." + echo "3. Asserts sequential DiscoveryUpload chunks over upload/discovery and parses PCAP magic headers." echo "4. Verifies Level 200 status reports in the container's discovery state." echo "" echo "Prerequisites:" @@ -157,12 +157,12 @@ cat < "$TEST_DIR/spotter_config.json" } EOF -echo "Setting up MQTT subscriber on port 1883 for state and stream event messages" +echo "Setting up MQTT subscriber on port 1883 for state and upload/discovery messages" MQTT_OUT="$TEST_DIR/mqtt.out" touch "$MQTT_OUT" mosquitto_sub -h 127.0.0.1 -p 1883 \ -t "/r/ZZ-TRI-FECTA/d/DSN-1/state" \ - -t "/r/ZZ-TRI-FECTA/d/DSN-1/events/streams" \ + -t "/r/ZZ-TRI-FECTA/d/DSN-1/upload/discovery" \ -F "%t %p" > "$MQTT_OUT" & MOSQ_SUB_PID=$! @@ -231,7 +231,7 @@ TRAFFIC_PID=$! echo "Publishing discovery TRACE config to trigger packet capture over MQTT..." mosquitto_pub -h 127.0.0.1 -p 1883 -t "/r/ZZ-TRI-FECTA/d/DSN-1/config" -f "$TEST_DIR/config_payload.json" -q 1 -echo "Waiting for discovery state 'stopped' and stream transfers" +echo "Waiting for discovery state 'stopped' and upload transfers" END_TIME=$((SECONDS + 25)) STATE_STOPPED=0 PCAP_DONE=0 @@ -243,10 +243,9 @@ while [[ $SECONDS -lt $END_TIME ]]; do fi fi - if grep -q '/events/streams' "$MQTT_OUT"; then - TOTAL=$(grep '/events/streams' "$MQTT_OUT" | head -n 1 | sed 's|^[^ ]* ||' | jq -r '.total_chunks') - COUNT=$(grep '/events/streams' "$MQTT_OUT" | wc -l) - if [[ -n "$TOTAL" && "$TOTAL" != "null" && $COUNT -ge $TOTAL ]]; then + if grep -q '/upload/discovery' "$MQTT_OUT"; then + EOF_EVENT=$(grep '/upload/discovery' "$MQTT_OUT" | sed 's|^[^ ]* ||' | jq -r 'select(.event_no < 0) | .event_no' | tail -n 1) + if [[ -n "$EOF_EVENT" ]]; then PCAP_DONE=1 fi fi @@ -265,7 +264,7 @@ if [[ $STATE_STOPPED -eq 0 ]]; then exit 1 fi if [[ $PCAP_DONE -eq 0 ]]; then - echo "ERROR: Test timed out waiting for PCAP streaming chunks over MQTT events/streams" + echo "ERROR: Test timed out waiting for PCAP streaming chunks over MQTT upload/discovery" exit 1 fi @@ -276,9 +275,9 @@ if grep '/state' "$MQTT_OUT" | tail -n 1 | sed 's|^[^ ]* ||' | jq -e '.discovery exit 1 fi -echo "Reassembling pcap payload using reassemble_pcap utility" -grep '/events/streams' "$MQTT_OUT" | sed 's|^[^ ]* ||' > "$TEST_DIR/stream_events.json" -"${SCRIPT_DIR}/reassemble_pcap" "$TEST_DIR/stream_events.json" "$TEST_DIR/assembled.pcap" +echo "Reassembling pcap payload using bin/mapper reassemble" +grep '/upload/discovery' "$MQTT_OUT" | sed 's|^[^ ]* ||' > "$TEST_DIR/stream_events.json" +"$WORKSPACE_DIR/bin/mapper" "$WORKSPACE_DIR/sites/udmi_site_model" //mqtt/localhost DSN-1 reassemble "$TEST_DIR/stream_events.json" "$TEST_DIR/assembled.pcap" echo "Validating pcap payload size" SIZE=$(stat -c%s "$TEST_DIR/assembled.pcap") diff --git a/edge/spotter/src/manager/discovery.py b/edge/spotter/src/manager/discovery.py index 91185b9c01..b3341dbaa7 100644 --- a/edge/spotter/src/manager/discovery.py +++ b/edge/spotter/src/manager/discovery.py @@ -2,11 +2,12 @@ Extends standard DiscoveryManager to natively handle both active protocol sweeps (BACnet, Ether, Passive) and TRACE-level packet streaming (PCAP) over -events/streams. +upload/discovery. """ import base64 from datetime import datetime, timedelta, timezone +import hashlib import logging import time from typing import Any, Dict, Optional @@ -16,10 +17,10 @@ from udmi.schema import ( Config, DiscoveryEvents, + DiscoveryUpload, Entry, FamilyDiscoveryConfig, FamilyDiscoveryState, - StreamsEvents, ) from udmi.schema.common import Depth from udmi.schema.state_discovery_family import Phase as DiscoveryPhase @@ -61,8 +62,8 @@ class SpotterDiscoveryManager(DiscoveryManager): """Unified Discovery Manager for Spotter. Extends standard DiscoveryManager to handle active protocol sweeps and - TRACE packet streaming over events/streams with safety circuit breaker checks - and UDMI discovery phase lifecycle tracking (pending/active/stopped). + TRACE packet streaming over upload/discovery with safety circuit breaker + checks and UDMI discovery phase lifecycle tracking (pending/active/stopped). """ def __init__(self, max_mem_pct: float = 85.0) -> None: @@ -364,7 +365,7 @@ def _run_scan(self, family: str, provider: Any) -> None: self.trigger_state_update() def _run_trace_capture(self, family: str, fam_config: Any) -> None: - """Executes PCAP capture and streams chunks over events/streams.""" + """Executes PCAP capture and streams chunks over upload/discovery.""" f_state = self._discovery_state.families.get(family) if not f_state: f_state = FamilyDiscoveryState() @@ -483,7 +484,7 @@ def _run_trace_capture(self, family: str, fam_config: Any) -> None: full_data = b"".join(captured_chunks) LOGGER.info( - "Capture complete (%d bytes). Emitting StreamsEvents...", + "Capture complete (%d bytes). Emitting DiscoveryUpload chunks...", len(full_data), ) chunk_size = 128 * 1024 # 128KB chunks @@ -491,39 +492,60 @@ def _run_trace_capture(self, family: str, fam_config: Any) -> None: total_chunks = ( (total_bytes + chunk_size - 1) // chunk_size if total_bytes > 0 else 1 ) - session_id = f"trace-{family}-{int(time.time())}" + gen_str = f_state.generation or _format_generation( + datetime.now(timezone.utc) + ) + + start_upload = DiscoveryUpload( + timestamp=datetime.now(timezone.utc).isoformat(), + version=UDMI_VERSION, + family=family, + generation=gen_str, + event_no=0, + ) + self.publish_upload(start_upload, "discovery") for idx in range(total_chunks): start = idx * chunk_size end = min(start + chunk_size, total_bytes) chunk_data = full_data[start:end] b64_data = base64.b64encode(chunk_data).decode() + event_no = idx + 1 - chunk_event = StreamsEvents( + chunk_upload = DiscoveryUpload( timestamp=datetime.now(timezone.utc).isoformat(), version=UDMI_VERSION, - session_id=session_id, - event_no=idx, - chunk_index=idx, - total_chunks=total_chunks, + family=family, + generation=gen_str, + event_no=event_no, data=b64_data, ) - self.publish_event(chunk_event, "streams") + self.publish_upload(chunk_upload, "discovery") LOGGER.info( - "Published stream chunk %d/%d (event_no: %d, %d bytes)", + "Published upload chunk %d/%d (event_no: %d, %d bytes)", idx + 1, total_chunks, - idx, + event_no, len(chunk_data), ) + eof_upload = DiscoveryUpload( + timestamp=datetime.now(timezone.utc).isoformat(), + version=UDMI_VERSION, + family=family, + generation=gen_str, + event_no=-(total_chunks + 1), + sha256=hashlib.sha256(full_data).hexdigest(), + ) + self.publish_upload(eof_upload, "discovery") + f_state.phase = DiscoveryPhase.stopped f_state.active_count = total_chunks f_state.status = Entry( category="discovery.family", level=200, message=( - f"Trace capture complete. {total_chunks} stream chunks emitted." + f"Trace capture complete. {total_chunks} upload chunks emitted." ), ) diff --git a/edge/spotter/tests/test_agent.py b/edge/spotter/tests/test_agent.py index 713006941b..4c17d30a16 100644 --- a/edge/spotter/tests/test_agent.py +++ b/edge/spotter/tests/test_agent.py @@ -452,16 +452,33 @@ def test_run_trace_worker_streaming(self, mock_capture): self.manager._discovery_state.families["ether"] = MagicMock() self.manager._run_trace_capture("ether", fam_config) - self.assertTrue(self.mock_dispatcher.publish_event.called) - call_args = self.mock_dispatcher.publish_event.call_args[0] - channel = call_args[0] - event_model = call_args[1] - - self.assertEqual(channel, "events/streams") - self.assertEqual(event_model.event_no, 0) - self.assertEqual(event_model.chunk_index, 0) - self.assertEqual(event_model.total_chunks, 1) - self.assertIn("trace-ether-", event_model.session_id) + self.assertEqual(self.mock_dispatcher.publish_event.call_count, 3) + calls = self.mock_dispatcher.publish_event.call_args_list + + # 1. Start marker (event_no = 0) + self.assertEqual(calls[0][0][0], "upload/discovery") + start_model = calls[0][0][1] + self.assertEqual(start_model.event_no, 0) + self.assertEqual(start_model.family, "ether") + self.assertIsNotNone(start_model.generation) + + # 2. Data chunk (event_no = 1) + self.assertEqual(calls[1][0][0], "upload/discovery") + chunk_model = calls[1][0][1] + self.assertEqual(chunk_model.event_no, 1) + self.assertEqual(chunk_model.family, "ether") + self.assertEqual(chunk_model.generation, start_model.generation) + + # 3. Terminal EOF marker (event_no = -2, sha256) + self.assertEqual(calls[2][0][0], "upload/discovery") + eof_model = calls[2][0][1] + self.assertEqual(eof_model.event_no, -2) + self.assertEqual(eof_model.family, "ether") + self.assertEqual(eof_model.generation, start_model.generation) + self.assertEqual( + eof_model.sha256, + hashlib.sha256(b"MOCK_PACKET_HEADERMOCK_PACKET_BODY").hexdigest(), + ) f_state = self.manager._discovery_state.families["ether"] self.assertEqual(f_state.phase, DiscoveryPhase.stopped) diff --git a/etc/sequencer.out b/etc/sequencer.out index 8d14ee33e1..2c15fa5ca9 100644 --- a/etc/sequencer.out +++ b/etc/sequencer.out @@ -6,6 +6,8 @@ RESULT pass discovery.scan scan_depth_entries+bacnet ALPHA 10/10 Sequence comple RESULT pass discovery.scan scan_depth_entries+vendor ALPHA 10/10 Sequence complete RESULT pass discovery.scan scan_depth_system+bacnet ALPHA 10/10 Sequence complete RESULT pass discovery.scan scan_depth_system+vendor ALPHA 10/10 Sequence complete +RESULT pass discovery.scan scan_depth_trace+bacnet ALPHA 10/10 Sequence complete +RESULT pass discovery.scan scan_depth_trace+vendor ALPHA 10/10 Sequence complete RESULT pass discovery.scan scan_network_single+bacnet ALPHA 10/10 Sequence complete RESULT pass discovery.scan scan_network_single+vendor ALPHA 10/10 Sequence complete RESULT pass discovery.scan scan_periodic_now_enumerate+bacnet PREVIEW 10/10 Sequence complete @@ -20,6 +22,7 @@ RESULT pass discovery.scan scan_single_targeted+bacnet ALPHA 10/10 Sequence comp RESULT pass discovery.scan scan_single_targeted+vendor ALPHA 10/10 Sequence complete RESULT pass endpoint.config endpoint_connection_bad_alternate PREVIEW 10/10 Sequence complete RESULT pass endpoint.config endpoint_connection_bad_hash PREVIEW 10/10 Sequence complete +RESULT pass endpoint.config endpoint_connection_download ALPHA 10/10 Sequence complete RESULT pass endpoint.config endpoint_connection_error PREVIEW 10/10 Sequence complete RESULT pass endpoint.config endpoint_connection_retry PREVIEW 10/10 Sequence complete RESULT pass endpoint.config endpoint_connection_success_alternate PREVIEW 10/10 Sequence complete @@ -51,6 +54,13 @@ RESULT pass system extra_config STABLE 10/10 Sequence complete RESULT pass system family_ether_addr PREVIEW 10/10 Sequence complete RESULT pass system family_ipv4_addr PREVIEW 10/10 Sequence complete RESULT skip system family_ipv6_addr PREVIEW 0/0 No ipv6 address defined in metadata +RESULT pass system state_make_model STABLE 10/10 Sequence complete +RESULT pass system state_software STABLE 10/10 Sequence complete +RESULT pass system system_last_update STABLE 11/11 Sequence complete +CPBLTY pass system system_last_update.subblocks ALPHA 1/1 Capability supported +RESULT pass system system_min_loglevel ALPHA 10/10 Sequence complete +RESULT pass system too_much_state ALPHA 10/10 Sequence complete +RESULT pass system valid_serial_no STABLE 10/10 Sequence complete RESULT pass system.mode system_mode_restart PREVIEW 10/10 Sequence complete RESULT pass system.software.updates blob_update_idempotency PREVIEW 10/10 Sequence complete RESULT pass system.software.updates blob_update_incompatible PREVIEW 10/10 Sequence complete @@ -59,13 +69,6 @@ RESULT pass system.software.updates blob_update_invalid_payload PREVIEW 10/10 Se RESULT pass system.software.updates blob_update_oversize PREVIEW 10/10 Sequence complete RESULT pass system.software.updates blob_update_success PREVIEW 10/10 Sequence complete RESULT pass system.software.updates blob_update_unreachable_url PREVIEW 10/10 Sequence complete -RESULT pass system state_make_model STABLE 10/10 Sequence complete -RESULT pass system state_software STABLE 10/10 Sequence complete -RESULT pass system system_last_update STABLE 11/11 Sequence complete -CPBLTY pass system system_last_update.subblocks ALPHA 1/1 Capability supported -RESULT pass system system_min_loglevel ALPHA 10/10 Sequence complete -RESULT pass system too_much_state ALPHA 10/10 Sequence complete -RESULT pass system valid_serial_no STABLE 10/10 Sequence complete RESULT pass writeback writeback_failure ALPHA 10/10 Sequence complete RESULT pass writeback writeback_invalid ALPHA 10/10 Sequence complete RESULT pass writeback writeback_operation ALPHA 10/10 Sequence complete diff --git a/gencode/docs/configuration_endpoint.html b/gencode/docs/configuration_endpoint.html index a7ec061281..5f74375004 100644 --- a/gencode/docs/configuration_endpoint.html +++ b/gencode/docs/configuration_endpoint.html @@ -1163,6 +1163,44 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+ + + + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
diff --git a/gencode/docs/configuration_execution.html b/gencode/docs/configuration_execution.html index c05580289a..21ac84905b 100644 --- a/gencode/docs/configuration_execution.html +++ b/gencode/docs/configuration_execution.html @@ -1853,6 +1853,51 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
@@ -3252,6 +3297,51 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
diff --git a/gencode/docs/configuration_pod.html b/gencode/docs/configuration_pod.html index d59cc3391c..397a283919 100644 --- a/gencode/docs/configuration_pod.html +++ b/gencode/docs/configuration_pod.html @@ -1611,6 +1611,51 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
@@ -3275,6 +3320,58 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
@@ -4943,6 +5040,58 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
@@ -6928,6 +7077,65 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
@@ -8789,6 +8997,65 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
@@ -10970,6 +11237,65 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
@@ -13151,6 +13477,65 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
diff --git a/gencode/docs/configuration_pubber.html b/gencode/docs/configuration_pubber.html index 1a0313579f..900b81d6ae 100644 --- a/gencode/docs/configuration_pubber.html +++ b/gencode/docs/configuration_pubber.html @@ -1419,6 +1419,51 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+ + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
diff --git a/gencode/docs/download_blobset.html b/gencode/docs/download_blobset.html new file mode 100644 index 0000000000..74f119c0a3 --- /dev/null +++ b/gencode/docs/download_blobset.html @@ -0,0 +1,1829 @@ + + + + + + + + + + + + + + + + Blobset Download + +
+ + +
+ +

Blobset Download

Type: object
+

Direct cloud-to-device payload delivery for blobset configurations over the download/blobset channel.

+
No Additional Properties + + + + + + +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

RFC 3339 UTC timestamp the download payload was generated

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
+
+
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Version of the UDMI schema

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: object
+

Map of system blobset keys to their provisioned endpoint configuration payloads

+
No Additional Properties + + + + + + +
+
+
+

+ +

+
+ +
+

+ +

+

All properties whose name matches the following regular expression must respect the following conditions

+ Property name regular expression: ^_[a-z_]+$ +
+ +

Endpoint Configuration

Type: object
+

Parameters to define a message endpoint

+
+ + No Additional Properties + + + + + + +
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Friendly name for this flow (debugging and diagnostics)

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: enum (of string)
+
+

Must be one of:

+
  • "local"
  • "pubsub"
  • "file"
  • "trace"
  • "mqtt"
+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: enum (of string)
+
+

Must be one of:

+
  • "ssl"
  • "tcp"
+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Simple payload template for simple injection use cases

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Error message container for capturing errors during parsing/handling

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: integer
+ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: integer
+

Delay waiting for config message on start, 0 for default, <0 to disable

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Prefix for message topics

+
Must match regular expression: ^[-_/a-zA-Z0-9]+$ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Id for the receiving message channel

+
Must match regular expression: ^[-_/a-zA-Z0-9#]+$ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Id for the sending messages channel

+
Must match regular expression: ^[-_/a-zA-Z0-9#]+$ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Id for a side-car message channel

+
Must match regular expression: ^[-_/a-zA-Z0-9#]+$ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Indicator if this endpoint should be active (null or non-empty)

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: boolean
+

True if config messages should not be acked (lower QOS)

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: integer
+

Queue capacity for limiting pipes

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: integer
+

Artifical publish delay for testing

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: integer
+

Rate for periodic task execution

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: object
+ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: object
+ No Additional Properties + + + + + + +
+
+
+

+ +

+
+ +
+
+ + Type: object
+ No Additional Properties + + + + + + +
+
+
+

+ +

+
+ +
+
+ + Type: string
+ + + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: object
+ No Additional Properties + + + + + + +
+
+
+

+ +

+
+ +
+
+ + Type: string
+ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Path to the CA certificate file

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Path to the client certificate file

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: string
+

Path to the client private key file

+
+ + + + + + +
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

The timestamp of the endpoint generation

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
+
+
+
+
+
+
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/gencode/docs/events.html b/gencode/docs/events.html index 618c455d31..68db87d7ce 100644 --- a/gencode/docs/events.html +++ b/gencode/docs/events.html @@ -25547,329 +25547,6 @@

- - - - - - - - -
-
-
-

- -

-
- -
-
- -

Streams Events

Type: object
-

Sequential data stream chunks for reliable transport over MQTT (e.g. PCAP traces, firmware blobs, reliable alarms, logs playback).

-
- - No Additional Properties - - - - - - -
-
-
-

- -

-
- -
-
- - Type: stringFormat: date-time
-

RFC 3339 UTC timestamp the stream chunk event was generated

-
- - - - - - -
-
-
-
-
-
-
-

- -

-
- -
-
- - Type: string
-

Version of the UDMI schema

-
- - - - - - -
-
-
-
-
-
-
-

- -

-
- -
-
- - Type: string
-

Unique session identifier for the continuous stream transmission

-
- - - - - - -
-
-
-
-
-
-
-

- -

-
- -
-
- - Type: integer
-

Sequence number for this stream event to support reliable sequential delivery and reassembly

-
- - - - - - -
-
-
-
-
-
-
-

- -

-
- -
-
- - Type: integer
-

0-based index of the transmitted chunk payload within the total session

-
- - - - - - -
-
-
-
-
-
-
-

- -

-
- -
-
- - Type: integer
-

Total number of chunks comprising the complete transmission binary

-
- - - - - - -
-
-
-
-
-
-
-

- -

-
- -
-
- - Type: string
-

Base64-encoded binary chunk payload data

-
- - - - - -
diff --git a/gencode/docs/monitoring.html b/gencode/docs/monitoring.html index 9bcd1299d8..e399bfba0e 100644 --- a/gencode/docs/monitoring.html +++ b/gencode/docs/monitoring.html @@ -4547,7 +4547,7 @@

subFolder

Type: enum (of string)

Must be one of:

-
  • "udmi"
  • "update"
  • "invalid"
  • "error"
  • "cloud"
  • "discovery"
  • "mapping"
  • "system"
  • "gateway"
  • "swarm"
  • "localnet"
  • "pointset"
  • "validation"
  • "blobset"
  • "monitoring"
  • "streams"
+
  • "udmi"
  • "update"
  • "invalid"
  • "error"
  • "cloud"
  • "discovery"
  • "mapping"
  • "system"
  • "gateway"
  • "swarm"
  • "localnet"
  • "pointset"
  • "validation"
  • "blobset"
  • "monitoring"
@@ -4597,7 +4597,7 @@

subType

Type: enum (of string)

Must be one of:

-
  • "invalid"
  • "local"
  • "reflect"
  • "events"
  • "errors"
  • "commands"
  • "config"
  • "state"
  • "query"
  • "reply"
  • "model"
  • "propose"
+
  • "invalid"
  • "local"
  • "reflect"
  • "events"
  • "errors"
  • "commands"
  • "config"
  • "state"
  • "query"
  • "reply"
  • "model"
  • "propose"
  • "upload"
  • "download"
diff --git a/gencode/docs/persistent_device.html b/gencode/docs/persistent_device.html index c627a9c45f..863d64e565 100644 --- a/gencode/docs/persistent_device.html +++ b/gencode/docs/persistent_device.html @@ -1419,6 +1419,51 @@

Example:

"2019-01-17T14:02:29.364Z"
 
+
+

+
+
+ +
+
+
+

+ +

+
+ +
+
+ + Type: stringFormat: date-time
+

Timestamp when the provisioned credential or endpoint expires

+
+ + + + + +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
diff --git a/gencode/docs/readme.md b/gencode/docs/readme.md index a4843f5eab..43db41ad43 100644 --- a/gencode/docs/readme.md +++ b/gencode/docs/readme.md @@ -7,13 +7,14 @@ ## Messages * [**config**](config.html) - The config block controls a device's intended behavior. [Config Documentation](../../docs/messages/config.md) +* [**download_blobset**](download_blobset.html) - Direct cloud-to-device payload delivery for blobset configurations over the download/blobset channel. * [**events_alarmset**](events_alarmset.html) - A set of alarms reporting telemetry data. [Alarmset Events Documentation](../../docs/messages/alarmset.md#telemetry) * [**events_discovery**](events_discovery.html) - [Discovery result](../../docs/specs/discovery.md) with implicit discovery * [**events_pointset**](events_pointset.html) - A set of points reporting telemetry data. [Pointset Events Documentation](../../docs/messages/pointset.md#telemetry) -* [**events_streams**](events_streams.html) - Sequential data stream chunks for reliable transport over MQTT (e.g. PCAP traces, firmware blobs, reliable alarms, logs playback). * [**events_system**](events_system.html) - Used for system events such as logging. [System Event Documentation](../../docs/messages/system.md#event) * [**events_udmi**](events_udmi.html) - Used for udmi events such as logging. * [**state**](state.html) - [State](../../docs/messages/state.md) message, defined by [`state.json`] +* [**upload_discovery**](upload_discovery.html) - Sequential binary chunk upload for discovery artifacts (e.g. PCAP traces) framed by config.discovery and state.discovery. ## Site Model * [**metadata**](metadata.html) - [Metadata](../../docs/specs/metadata.md) is a description about the device: a specification about how the device should be configured and expectations about what the device should be doing. Defined by `metadata.json` diff --git a/gencode/docs/events_streams.html b/gencode/docs/upload_discovery.html similarity index 72% rename from gencode/docs/events_streams.html rename to gencode/docs/upload_discovery.html index 75f0bea9e5..31a77df1c2 100644 --- a/gencode/docs/events_streams.html +++ b/gencode/docs/upload_discovery.html @@ -13,15 +13,15 @@ - Streams Events + Discovery Upload
-

Streams Events

Type: object
-

Sequential data stream chunks for reliable transport over MQTT (e.g. PCAP traces, firmware blobs, reliable alarms, logs playback).

+

Discovery Upload

Type: object
+

Sequential binary chunk upload for discovery artifacts (e.g. PCAP traces) framed by config.discovery and state.discovery.

No Additional Properties @@ -34,7 +34,7 @@

+ aria-expanded="" aria-controls="timestamp" onclick="setAnchor('#timestamp')">timestamp Required

@@ -51,14 +51,18 @@

/> timestamp

Type: stringFormat: date-time
-

RFC 3339 UTC timestamp the stream chunk event was generated

+

RFC 3339 UTC timestamp the upload chunk was generated

- +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
+
@@ -68,7 +72,7 @@

+ aria-expanded="" aria-controls="version" onclick="setAnchor('#version')">version Required

@@ -97,18 +101,18 @@

-
+
-
+

- +

-
+
Type: string
-

Unique session identifier for the continuous stream transmission

+ family
Type: string
+

Protocol family associated with the discovery trace upload

@@ -131,18 +135,18 @@

-
+
-
+

- +

-
+
Type: integer
-

Sequence number for this stream event to support reliable sequential delivery and reassembly

+ generation
Type: stringFormat: date-time
+

The discovery scan trigger's generation timestamp

- +
+
Example:
+
"2019-01-17T14:02:29.364Z"
+
+
-
+
-
+

- +

-
+
Type: integer
-

0-based index of the transmitted chunk payload within the total session

+ event_no
Type: integer
+

Sequence number for this upload chunk: 0 for start marker, 1..N for data chunks, -(N + 1) for terminal EOF marker

@@ -199,18 +207,18 @@

-
+
-
+

- +

-
+
Type: integer
-

Total number of chunks comprising the complete transmission binary

-
+ sha256
Type: string
+

SHA-256 hex digest of the complete unencoded binary payload (included on the terminal negative event_no marker)

+
Must match regular expression: ^[0-9a-f]{64}$ diff --git a/gencode/java/udmi/schema/BlobsetDownload.java b/gencode/java/udmi/schema/BlobsetDownload.java new file mode 100644 index 0000000000..f69fd99289 --- /dev/null +++ b/gencode/java/udmi/schema/BlobsetDownload.java @@ -0,0 +1,67 @@ + +package udmi.schema; + +import java.util.Date; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + + +/** + * Blobset Download + *

+ * Direct cloud-to-device payload delivery for blobset configurations over the download/blobset channel. + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BlobsetDownload { + + /** + * RFC 3339 UTC timestamp the download payload was generated + * (Required) + * + */ + @JsonProperty("timestamp") + @JsonPropertyDescription("RFC 3339 UTC timestamp the download payload was generated") + public Date timestamp; + /** + * Version of the UDMI schema + * (Required) + * + */ + @JsonProperty("version") + @JsonPropertyDescription("Version of the UDMI schema") + public java.lang.String version; + /** + * Map of system blobset keys to their provisioned endpoint configuration payloads + * (Required) + * + */ + @JsonProperty("blobs") + @JsonPropertyDescription("Map of system blobset keys to their provisioned endpoint configuration payloads") + public HashMap blobs; + + @Override + public int hashCode() { + int result = 1; + result = ((result* 31)+((this.version == null)? 0 :this.version.hashCode())); + result = ((result* 31)+((this.blobs == null)? 0 :this.blobs.hashCode())); + result = ((result* 31)+((this.timestamp == null)? 0 :this.timestamp.hashCode())); + return result; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if ((other instanceof BlobsetDownload) == false) { + return false; + } + BlobsetDownload rhs = ((BlobsetDownload) other); + return ((((this.version == rhs.version)||((this.version!= null)&&this.version.equals(rhs.version)))&&((this.blobs == rhs.blobs)||((this.blobs!= null)&&this.blobs.equals(rhs.blobs))))&&((this.timestamp == rhs.timestamp)||((this.timestamp!= null)&&this.timestamp.equals(rhs.timestamp)))); + } + +} diff --git a/gencode/java/udmi/schema/DiscoveryUpload.java b/gencode/java/udmi/schema/DiscoveryUpload.java new file mode 100644 index 0000000000..09e174fd7e --- /dev/null +++ b/gencode/java/udmi/schema/DiscoveryUpload.java @@ -0,0 +1,100 @@ + +package udmi.schema; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + + +/** + * Discovery Upload + *

+ * Sequential binary chunk upload for discovery artifacts (e.g. PCAP traces) framed by config.discovery and state.discovery. + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DiscoveryUpload { + + /** + * RFC 3339 UTC timestamp the upload chunk was generated + * (Required) + * + */ + @JsonProperty("timestamp") + @JsonPropertyDescription("RFC 3339 UTC timestamp the upload chunk was generated") + public Date timestamp; + /** + * Version of the UDMI schema + * (Required) + * + */ + @JsonProperty("version") + @JsonPropertyDescription("Version of the UDMI schema") + public String version; + /** + * Protocol family associated with the discovery trace upload + * (Required) + * + */ + @JsonProperty("family") + @JsonPropertyDescription("Protocol family associated with the discovery trace upload") + public String family; + /** + * The discovery scan trigger's generation timestamp + * (Required) + * + */ + @JsonProperty("generation") + @JsonPropertyDescription("The discovery scan trigger's generation timestamp") + public Date generation; + /** + * Sequence number for this upload chunk: 0 for start marker, 1..N for data chunks, -(N + 1) for terminal EOF marker + * (Required) + * + */ + @JsonProperty("event_no") + @JsonPropertyDescription("Sequence number for this upload chunk: 0 for start marker, 1..N for data chunks, -(N + 1) for terminal EOF marker") + public Integer event_no; + /** + * SHA-256 hex digest of the complete unencoded binary payload (included on the terminal negative event_no marker) + * + */ + @JsonProperty("sha256") + @JsonPropertyDescription("SHA-256 hex digest of the complete unencoded binary payload (included on the terminal negative event_no marker)") + public String sha256; + /** + * Base64-encoded binary chunk payload data + * + */ + @JsonProperty("data") + @JsonPropertyDescription("Base64-encoded binary chunk payload data") + public String data; + + @Override + public int hashCode() { + int result = 1; + result = ((result* 31)+((this.generation == null)? 0 :this.generation.hashCode())); + result = ((result* 31)+((this.sha256 == null)? 0 :this.sha256 .hashCode())); + result = ((result* 31)+((this.data == null)? 0 :this.data.hashCode())); + result = ((result* 31)+((this.event_no == null)? 0 :this.event_no.hashCode())); + result = ((result* 31)+((this.family == null)? 0 :this.family.hashCode())); + result = ((result* 31)+((this.version == null)? 0 :this.version.hashCode())); + result = ((result* 31)+((this.timestamp == null)? 0 :this.timestamp.hashCode())); + return result; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if ((other instanceof DiscoveryUpload) == false) { + return false; + } + DiscoveryUpload rhs = ((DiscoveryUpload) other); + return ((((((((this.generation == rhs.generation)||((this.generation!= null)&&this.generation.equals(rhs.generation)))&&((this.sha256 == rhs.sha256)||((this.sha256 != null)&&this.sha256 .equals(rhs.sha256))))&&((this.data == rhs.data)||((this.data!= null)&&this.data.equals(rhs.data))))&&((this.event_no == rhs.event_no)||((this.event_no!= null)&&this.event_no.equals(rhs.event_no))))&&((this.family == rhs.family)||((this.family!= null)&&this.family.equals(rhs.family))))&&((this.version == rhs.version)||((this.version!= null)&&this.version.equals(rhs.version))))&&((this.timestamp == rhs.timestamp)||((this.timestamp!= null)&&this.timestamp.equals(rhs.timestamp)))); + } + +} diff --git a/gencode/java/udmi/schema/EndpointConfiguration.java b/gencode/java/udmi/schema/EndpointConfiguration.java index 81ab05cee6..ec6d589e07 100644 --- a/gencode/java/udmi/schema/EndpointConfiguration.java +++ b/gencode/java/udmi/schema/EndpointConfiguration.java @@ -175,6 +175,13 @@ public class EndpointConfiguration { @JsonProperty("generation") @JsonPropertyDescription("The timestamp of the endpoint generation") public Date generation; + /** + * Timestamp when the provisioned credential or endpoint expires + * + */ + @JsonProperty("expiry") + @JsonPropertyDescription("Timestamp when the provisioned credential or endpoint expires") + public Date expiry; @Override public int hashCode() { @@ -193,6 +200,7 @@ public int hashCode() { result = ((result* 31)+((this.hostname == null)? 0 :this.hostname.hashCode())); result = ((result* 31)+((this.payload == null)? 0 :this.payload.hashCode())); result = ((result* 31)+((this.topic_prefix == null)? 0 :this.topic_prefix.hashCode())); + result = ((result* 31)+((this.expiry == null)? 0 :this.expiry.hashCode())); result = ((result* 31)+((this.recv_id == null)? 0 :this.recv_id.hashCode())); result = ((result* 31)+((this.algorithm == null)? 0 :this.algorithm.hashCode())); result = ((result* 31)+((this.generation == null)? 0 :this.generation.hashCode())); @@ -218,7 +226,7 @@ public boolean equals(Object other) { return false; } EndpointConfiguration rhs = ((EndpointConfiguration) other); - return ((((((((((((((((((((((((((((this.keyBytes == rhs.keyBytes)||((this.keyBytes!= null)&&this.keyBytes.equals(rhs.keyBytes)))&&((this.ca_file == rhs.ca_file)||((this.ca_file!= null)&&this.ca_file.equals(rhs.ca_file))))&&((this.key_file == rhs.key_file)||((this.key_file!= null)&&this.key_file.equals(rhs.key_file))))&&((this.cert_file == rhs.cert_file)||((this.cert_file!= null)&&this.cert_file.equals(rhs.cert_file))))&&((this.side_id == rhs.side_id)||((this.side_id!= null)&&this.side_id.equals(rhs.side_id))))&&((this.error == rhs.error)||((this.error!= null)&&this.error.equals(rhs.error))))&&((this.deviceId == rhs.deviceId)||((this.deviceId!= null)&&this.deviceId.equals(rhs.deviceId))))&&((this.client_id == rhs.client_id)||((this.client_id!= null)&&this.client_id.equals(rhs.client_id))))&&((this.enabled == rhs.enabled)||((this.enabled!= null)&&this.enabled.equals(rhs.enabled))))&&((this.capacity == rhs.capacity)||((this.capacity!= null)&&this.capacity.equals(rhs.capacity))))&&((this.protocol == rhs.protocol)||((this.protocol!= null)&&this.protocol.equals(rhs.protocol))))&&((this.hostname == rhs.hostname)||((this.hostname!= null)&&this.hostname.equals(rhs.hostname))))&&((this.payload == rhs.payload)||((this.payload!= null)&&this.payload.equals(rhs.payload))))&&((this.topic_prefix == rhs.topic_prefix)||((this.topic_prefix!= null)&&this.topic_prefix.equals(rhs.topic_prefix))))&&((this.recv_id == rhs.recv_id)||((this.recv_id!= null)&&this.recv_id.equals(rhs.recv_id))))&&((this.algorithm == rhs.algorithm)||((this.algorithm!= null)&&this.algorithm.equals(rhs.algorithm))))&&((this.generation == rhs.generation)||((this.generation!= null)&&this.generation.equals(rhs.generation))))&&((this.transport == rhs.transport)||((this.transport!= null)&&this.transport.equals(rhs.transport))))&&((this.publish_delay_sec == rhs.publish_delay_sec)||((this.publish_delay_sec!= null)&&this.publish_delay_sec.equals(rhs.publish_delay_sec))))&&((this.config_sync_sec == rhs.config_sync_sec)||((this.config_sync_sec!= null)&&this.config_sync_sec.equals(rhs.config_sync_sec))))&&((this.send_id == rhs.send_id)||((this.send_id!= null)&&this.send_id.equals(rhs.send_id))))&&((this.port == rhs.port)||((this.port!= null)&&this.port.equals(rhs.port))))&&((this.name == rhs.name)||((this.name!= null)&&this.name.equals(rhs.name))))&&((this.periodic_sec == rhs.periodic_sec)||((this.periodic_sec!= null)&&this.periodic_sec.equals(rhs.periodic_sec))))&&((this.noConfigAck == rhs.noConfigAck)||((this.noConfigAck!= null)&&this.noConfigAck.equals(rhs.noConfigAck))))&&((this.gatewayId == rhs.gatewayId)||((this.gatewayId!= null)&&this.gatewayId.equals(rhs.gatewayId))))&&((this.auth_provider == rhs.auth_provider)||((this.auth_provider!= null)&&this.auth_provider.equals(rhs.auth_provider)))); + return (((((((((((((((((((((((((((((this.keyBytes == rhs.keyBytes)||((this.keyBytes!= null)&&this.keyBytes.equals(rhs.keyBytes)))&&((this.ca_file == rhs.ca_file)||((this.ca_file!= null)&&this.ca_file.equals(rhs.ca_file))))&&((this.key_file == rhs.key_file)||((this.key_file!= null)&&this.key_file.equals(rhs.key_file))))&&((this.cert_file == rhs.cert_file)||((this.cert_file!= null)&&this.cert_file.equals(rhs.cert_file))))&&((this.side_id == rhs.side_id)||((this.side_id!= null)&&this.side_id.equals(rhs.side_id))))&&((this.error == rhs.error)||((this.error!= null)&&this.error.equals(rhs.error))))&&((this.deviceId == rhs.deviceId)||((this.deviceId!= null)&&this.deviceId.equals(rhs.deviceId))))&&((this.client_id == rhs.client_id)||((this.client_id!= null)&&this.client_id.equals(rhs.client_id))))&&((this.enabled == rhs.enabled)||((this.enabled!= null)&&this.enabled.equals(rhs.enabled))))&&((this.capacity == rhs.capacity)||((this.capacity!= null)&&this.capacity.equals(rhs.capacity))))&&((this.protocol == rhs.protocol)||((this.protocol!= null)&&this.protocol.equals(rhs.protocol))))&&((this.hostname == rhs.hostname)||((this.hostname!= null)&&this.hostname.equals(rhs.hostname))))&&((this.payload == rhs.payload)||((this.payload!= null)&&this.payload.equals(rhs.payload))))&&((this.topic_prefix == rhs.topic_prefix)||((this.topic_prefix!= null)&&this.topic_prefix.equals(rhs.topic_prefix))))&&((this.expiry == rhs.expiry)||((this.expiry!= null)&&this.expiry.equals(rhs.expiry))))&&((this.recv_id == rhs.recv_id)||((this.recv_id!= null)&&this.recv_id.equals(rhs.recv_id))))&&((this.algorithm == rhs.algorithm)||((this.algorithm!= null)&&this.algorithm.equals(rhs.algorithm))))&&((this.generation == rhs.generation)||((this.generation!= null)&&this.generation.equals(rhs.generation))))&&((this.transport == rhs.transport)||((this.transport!= null)&&this.transport.equals(rhs.transport))))&&((this.publish_delay_sec == rhs.publish_delay_sec)||((this.publish_delay_sec!= null)&&this.publish_delay_sec.equals(rhs.publish_delay_sec))))&&((this.config_sync_sec == rhs.config_sync_sec)||((this.config_sync_sec!= null)&&this.config_sync_sec.equals(rhs.config_sync_sec))))&&((this.send_id == rhs.send_id)||((this.send_id!= null)&&this.send_id.equals(rhs.send_id))))&&((this.port == rhs.port)||((this.port!= null)&&this.port.equals(rhs.port))))&&((this.name == rhs.name)||((this.name!= null)&&this.name.equals(rhs.name))))&&((this.periodic_sec == rhs.periodic_sec)||((this.periodic_sec!= null)&&this.periodic_sec.equals(rhs.periodic_sec))))&&((this.noConfigAck == rhs.noConfigAck)||((this.noConfigAck!= null)&&this.noConfigAck.equals(rhs.noConfigAck))))&&((this.gatewayId == rhs.gatewayId)||((this.gatewayId!= null)&&this.gatewayId.equals(rhs.gatewayId))))&&((this.auth_provider == rhs.auth_provider)||((this.auth_provider!= null)&&this.auth_provider.equals(rhs.auth_provider)))); } public enum Protocol { diff --git a/gencode/java/udmi/schema/Envelope.java b/gencode/java/udmi/schema/Envelope.java index 28944ac793..923211601a 100644 --- a/gencode/java/udmi/schema/Envelope.java +++ b/gencode/java/udmi/schema/Envelope.java @@ -111,8 +111,7 @@ public enum SubFolder { POINTSET("pointset"), VALIDATION("validation"), BLOBSET("blobset"), - MONITORING("monitoring"), - STREAMS("streams"); + MONITORING("monitoring"); private final String value; private final static Map CONSTANTS = new HashMap(); @@ -161,7 +160,9 @@ public enum SubType { QUERY("query"), REPLY("reply"), MODEL("model"), - PROPOSE("propose"); + PROPOSE("propose"), + UPLOAD("upload"), + DOWNLOAD("download"); private final String value; private final static Map CONSTANTS = new HashMap(); diff --git a/gencode/java/udmi/schema/Events.java b/gencode/java/udmi/schema/Events.java index 463c9a60e5..f958acd7ce 100644 --- a/gencode/java/udmi/schema/Events.java +++ b/gencode/java/udmi/schema/Events.java @@ -43,23 +43,13 @@ public class Events { @JsonProperty("discovery") @JsonPropertyDescription("[Discovery result](../docs/specs/discovery.md) with implicit discovery") public DiscoveryEvents discovery; - /** - * Streams Events - *

- * Sequential data stream chunks for reliable transport over MQTT (e.g. PCAP traces, firmware blobs, reliable alarms, logs playback). - * - */ - @JsonProperty("streams") - @JsonPropertyDescription("Sequential data stream chunks for reliable transport over MQTT (e.g. PCAP traces, firmware blobs, reliable alarms, logs playback).") - public StreamsEvents streams; @Override public int hashCode() { int result = 1; result = ((result* 31)+((this.pointset == null)? 0 :this.pointset.hashCode())); - result = ((result* 31)+((this.system == null)? 0 :this.system.hashCode())); result = ((result* 31)+((this.discovery == null)? 0 :this.discovery.hashCode())); - result = ((result* 31)+((this.streams == null)? 0 :this.streams.hashCode())); + result = ((result* 31)+((this.system == null)? 0 :this.system.hashCode())); return result; } @@ -72,7 +62,7 @@ public boolean equals(Object other) { return false; } Events rhs = ((Events) other); - return (((((this.pointset == rhs.pointset)||((this.pointset!= null)&&this.pointset.equals(rhs.pointset)))&&((this.system == rhs.system)||((this.system!= null)&&this.system.equals(rhs.system))))&&((this.discovery == rhs.discovery)||((this.discovery!= null)&&this.discovery.equals(rhs.discovery))))&&((this.streams == rhs.streams)||((this.streams!= null)&&this.streams.equals(rhs.streams)))); + return ((((this.pointset == rhs.pointset)||((this.pointset!= null)&&this.pointset.equals(rhs.pointset)))&&((this.discovery == rhs.discovery)||((this.discovery!= null)&&this.discovery.equals(rhs.discovery))))&&((this.system == rhs.system)||((this.system!= null)&&this.system.equals(rhs.system)))); } } diff --git a/gencode/java/udmi/schema/StreamsEvents.java b/gencode/java/udmi/schema/StreamsEvents.java deleted file mode 100644 index 4794855dd9..0000000000 --- a/gencode/java/udmi/schema/StreamsEvents.java +++ /dev/null @@ -1,95 +0,0 @@ - -package udmi.schema; - -import java.util.Date; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; - - -/** - * Streams Events - *

- * Sequential data stream chunks for reliable transport over MQTT (e.g. PCAP traces, firmware blobs, reliable alarms, logs playback). - * - */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public class StreamsEvents { - - /** - * RFC 3339 UTC timestamp the stream chunk event was generated - * - */ - @JsonProperty("timestamp") - @JsonPropertyDescription("RFC 3339 UTC timestamp the stream chunk event was generated") - public Date timestamp; - /** - * Version of the UDMI schema - * - */ - @JsonProperty("version") - @JsonPropertyDescription("Version of the UDMI schema") - public String version; - /** - * Unique session identifier for the continuous stream transmission - * - */ - @JsonProperty("session_id") - @JsonPropertyDescription("Unique session identifier for the continuous stream transmission") - public String session_id; - /** - * Sequence number for this stream event to support reliable sequential delivery and reassembly - * - */ - @JsonProperty("event_no") - @JsonPropertyDescription("Sequence number for this stream event to support reliable sequential delivery and reassembly") - public Integer event_no; - /** - * 0-based index of the transmitted chunk payload within the total session - * - */ - @JsonProperty("chunk_index") - @JsonPropertyDescription("0-based index of the transmitted chunk payload within the total session") - public Integer chunk_index; - /** - * Total number of chunks comprising the complete transmission binary - * - */ - @JsonProperty("total_chunks") - @JsonPropertyDescription("Total number of chunks comprising the complete transmission binary") - public Integer total_chunks; - /** - * Base64-encoded binary chunk payload data - * - */ - @JsonProperty("data") - @JsonPropertyDescription("Base64-encoded binary chunk payload data") - public String data; - - @Override - public int hashCode() { - int result = 1; - result = ((result* 31)+((this.chunk_index == null)? 0 :this.chunk_index.hashCode())); - result = ((result* 31)+((this.data == null)? 0 :this.data.hashCode())); - result = ((result* 31)+((this.event_no == null)? 0 :this.event_no.hashCode())); - result = ((result* 31)+((this.session_id == null)? 0 :this.session_id.hashCode())); - result = ((result* 31)+((this.version == null)? 0 :this.version.hashCode())); - result = ((result* 31)+((this.total_chunks == null)? 0 :this.total_chunks.hashCode())); - result = ((result* 31)+((this.timestamp == null)? 0 :this.timestamp.hashCode())); - return result; - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if ((other instanceof StreamsEvents) == false) { - return false; - } - StreamsEvents rhs = ((StreamsEvents) other); - return ((((((((this.chunk_index == rhs.chunk_index)||((this.chunk_index!= null)&&this.chunk_index.equals(rhs.chunk_index)))&&((this.data == rhs.data)||((this.data!= null)&&this.data.equals(rhs.data))))&&((this.event_no == rhs.event_no)||((this.event_no!= null)&&this.event_no.equals(rhs.event_no))))&&((this.session_id == rhs.session_id)||((this.session_id!= null)&&this.session_id.equals(rhs.session_id))))&&((this.version == rhs.version)||((this.version!= null)&&this.version.equals(rhs.version))))&&((this.total_chunks == rhs.total_chunks)||((this.total_chunks!= null)&&this.total_chunks.equals(rhs.total_chunks))))&&((this.timestamp == rhs.timestamp)||((this.timestamp!= null)&&this.timestamp.equals(rhs.timestamp)))); - } - -} diff --git a/gencode/presentation/presentation.json b/gencode/presentation/presentation.json index 98cb150aa0..830011e76d 100644 --- a/gencode/presentation/presentation.json +++ b/gencode/presentation/presentation.json @@ -471,6 +471,16 @@ "2019-01-17T14:02:29.364Z" ] }, + "reflector_endpoint.expiry": { + "display": "show", + "style": "bold", + "section": "cloud_iot_config", + "type": "string", + "description": "Timestamp when the provisioned credential or endpoint expires", + "examples": [ + "2019-01-17T14:02:29.364Z" + ] + }, "device_endpoint.name": { "display": "show", "style": "bold", @@ -644,6 +654,16 @@ "2019-01-17T14:02:29.364Z" ] }, + "device_endpoint.expiry": { + "display": "show", + "style": "bold", + "section": "cloud_iot_config", + "type": "string", + "description": "Timestamp when the provisioned credential or endpoint expires", + "examples": [ + "2019-01-17T14:02:29.364Z" + ] + }, "project_id": { "display": "show", "style": "bold", diff --git a/gencode/python/udmi/schema/__init__.py b/gencode/python/udmi/schema/__init__.py index f7e8dd0b75..58a191810e 100644 --- a/gencode/python/udmi/schema/__init__.py +++ b/gencode/python/udmi/schema/__init__.py @@ -34,6 +34,7 @@ from .discovery_family import * from .discovery_feature import * from .discovery_ref import * +from .download_blobset import * from .entry import * from .envelope import * from .equipment_translation import * @@ -43,7 +44,6 @@ from .events_discovery import * from .events_pointset import * from .events_pointset_point import * -from .events_streams import * from .events_system import * from .events_udmi import * from .events_validation import * @@ -101,3 +101,4 @@ from .state_validation_feature import * from .state_validation_schema import * from .state_validation_sequence import * +from .upload_discovery import * diff --git a/gencode/python/udmi/schema/configuration_endpoint.py b/gencode/python/udmi/schema/configuration_endpoint.py index 954286596b..0750d3c801 100644 --- a/gencode/python/udmi/schema/configuration_endpoint.py +++ b/gencode/python/udmi/schema/configuration_endpoint.py @@ -73,3 +73,4 @@ class EndpointConfiguration(DataModel): cert_file: Optional[str] = None key_file: Optional[str] = None generation: Optional[str] = None + expiry: Optional[str] = None diff --git a/gencode/python/udmi/schema/download_blobset.py b/gencode/python/udmi/schema/download_blobset.py new file mode 100644 index 0000000000..a583146b4d --- /dev/null +++ b/gencode/python/udmi/schema/download_blobset.py @@ -0,0 +1,21 @@ +# generated by datamodel-codegen: +# filename: download_blobset.json + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Dict, Optional + +from ._base import DataModel +from .configuration_endpoint import EndpointConfiguration + + +@dataclass +class BlobsetDownload(DataModel): + """ + Direct cloud-to-device payload delivery for blobset configurations over the download/blobset channel. + """ + + timestamp: Optional[str] = None + version: Optional[str] = None + blobs: Optional[Dict[str, EndpointConfiguration]] = None diff --git a/gencode/python/udmi/schema/envelope.py b/gencode/python/udmi/schema/envelope.py index 08cdb8509e..d363fcdffb 100644 --- a/gencode/python/udmi/schema/envelope.py +++ b/gencode/python/udmi/schema/envelope.py @@ -26,7 +26,6 @@ class SubFolder(Enum): validation = 'validation' blobset = 'blobset' monitoring = 'monitoring' - streams = 'streams' class SubType(Enum): @@ -42,6 +41,8 @@ class SubType(Enum): reply = 'reply' model = 'model' propose = 'propose' + upload = 'upload' + download = 'download' @dataclass diff --git a/gencode/python/udmi/schema/events.py b/gencode/python/udmi/schema/events.py index 3ac694d27a..d932e899e5 100644 --- a/gencode/python/udmi/schema/events.py +++ b/gencode/python/udmi/schema/events.py @@ -9,7 +9,6 @@ from ._base import DataModel from .events_discovery import DiscoveryEvents from .events_pointset import PointsetEvents -from .events_streams import StreamsEvents from .events_system import SystemEvents @@ -22,4 +21,3 @@ class Events(DataModel): system: Optional[SystemEvents] = None pointset: Optional[PointsetEvents] = None discovery: Optional[DiscoveryEvents] = None - streams: Optional[StreamsEvents] = None diff --git a/gencode/python/udmi/schema/events_streams.py b/gencode/python/udmi/schema/upload_discovery.py similarity index 51% rename from gencode/python/udmi/schema/events_streams.py rename to gencode/python/udmi/schema/upload_discovery.py index e1f344a814..f7c5cebf5d 100644 --- a/gencode/python/udmi/schema/events_streams.py +++ b/gencode/python/udmi/schema/upload_discovery.py @@ -1,5 +1,5 @@ # generated by datamodel-codegen: -# filename: events_streams.json +# filename: upload_discovery.json from __future__ import annotations @@ -10,15 +10,15 @@ @dataclass -class StreamsEvents(DataModel): +class DiscoveryUpload(DataModel): """ - Sequential data stream chunks for reliable transport over MQTT (e.g. PCAP traces, firmware blobs, reliable alarms, logs playback). + Sequential binary chunk upload for discovery artifacts (e.g. PCAP traces) framed by config.discovery and state.discovery. """ timestamp: Optional[str] = None version: Optional[str] = None - session_id: Optional[str] = None + family: Optional[str] = None + generation: Optional[str] = None event_no: Optional[int] = None - chunk_index: Optional[int] = None - total_chunks: Optional[int] = None + sha256: Optional[str] = None data: Optional[str] = None diff --git a/misc/bambi/presentation.gld.json b/misc/bambi/presentation.gld.json index 98cb150aa0..830011e76d 100644 --- a/misc/bambi/presentation.gld.json +++ b/misc/bambi/presentation.gld.json @@ -471,6 +471,16 @@ "2019-01-17T14:02:29.364Z" ] }, + "reflector_endpoint.expiry": { + "display": "show", + "style": "bold", + "section": "cloud_iot_config", + "type": "string", + "description": "Timestamp when the provisioned credential or endpoint expires", + "examples": [ + "2019-01-17T14:02:29.364Z" + ] + }, "device_endpoint.name": { "display": "show", "style": "bold", @@ -644,6 +654,16 @@ "2019-01-17T14:02:29.364Z" ] }, + "device_endpoint.expiry": { + "display": "show", + "style": "bold", + "section": "cloud_iot_config", + "type": "string", + "description": "Timestamp when the provisioned credential or endpoint expires", + "examples": [ + "2019-01-17T14:02:29.364Z" + ] + }, "project_id": { "display": "show", "style": "bold", diff --git a/pubber/src/main/java/udmi/lib/base/MqttDevice.java b/pubber/src/main/java/udmi/lib/base/MqttDevice.java index 804f6e6912..dc8a82ead9 100644 --- a/pubber/src/main/java/udmi/lib/base/MqttDevice.java +++ b/pubber/src/main/java/udmi/lib/base/MqttDevice.java @@ -20,6 +20,8 @@ public class MqttDevice { public static final String ERRORS_TOPIC = "errors"; public static final String EVENTS_TOPIC = "events"; public static final String STATE_TOPIC = "state"; + public static final String DOWNLOAD_TOPIC = "download/blobset"; + public static final String UPLOAD_TOPIC = "upload"; private final String deviceId; private final Publisher publisher; diff --git a/pubber/src/main/java/udmi/lib/base/MqttPublisher.java b/pubber/src/main/java/udmi/lib/base/MqttPublisher.java index a2605f6067..6a6981edd9 100644 --- a/pubber/src/main/java/udmi/lib/base/MqttPublisher.java +++ b/pubber/src/main/java/udmi/lib/base/MqttPublisher.java @@ -498,6 +498,8 @@ private void subscribeToUpdates(MqttClient client, String deviceId) { if (configuration.recv_id == null) { subscribeTopic(client, getMessageTopic(deviceId, MqttDevice.CONFIG_TOPIC), configQos); subscribeTopic(client, getMessageTopic(deviceId, MqttDevice.ERRORS_TOPIC), QOS_AT_MOST_ONCE); + subscribeTopic( + client, getMessageTopic(deviceId, MqttDevice.DOWNLOAD_TOPIC), QOS_AT_MOST_ONCE); } else { subscribeTopic(client, configuration.recv_id, configQos); } diff --git a/pubber/src/main/java/udmi/lib/client/host/PublisherHost.java b/pubber/src/main/java/udmi/lib/client/host/PublisherHost.java index 0d7c3e88a0..d5e0d13996 100644 --- a/pubber/src/main/java/udmi/lib/client/host/PublisherHost.java +++ b/pubber/src/main/java/udmi/lib/client/host/PublisherHost.java @@ -10,6 +10,7 @@ import static com.google.udmi.util.GeneralUtils.ifNotNullGet; import static com.google.udmi.util.GeneralUtils.ifNotNullThen; import static com.google.udmi.util.GeneralUtils.ifTrueThen; +import static com.google.udmi.util.GeneralUtils.isNotEmpty; import static com.google.udmi.util.GeneralUtils.setClockSkew; import static com.google.udmi.util.GeneralUtils.stackTraceString; import static com.google.udmi.util.GeneralUtils.toJsonFile; @@ -18,11 +19,13 @@ import static com.google.udmi.util.JsonUtil.parseJson; import static com.google.udmi.util.JsonUtil.safeSleep; import static com.google.udmi.util.JsonUtil.stringify; +import static com.google.udmi.util.JsonUtil.stringifyTerse; import static java.lang.String.format; import static java.util.Optional.ofNullable; import static udmi.lib.base.ManagerBase.INITIAL_THRESHOLD_SEC; import static udmi.lib.base.ManagerBase.updateStateHolder; import static udmi.lib.base.MqttDevice.CONFIG_TOPIC; +import static udmi.lib.base.MqttDevice.DOWNLOAD_TOPIC; import static udmi.lib.base.MqttDevice.ERRORS_TOPIC; import static udmi.lib.base.MqttDevice.STATE_TOPIC; import static udmi.lib.base.MqttPublisher.DEFAULT_CONFIG_WAIT_SEC; @@ -75,11 +78,13 @@ import udmi.schema.BlobBlobsetState; import udmi.schema.BlobsetConfig; import udmi.schema.BlobsetConfig.SystemBlobsets; +import udmi.schema.BlobsetDownload; import udmi.schema.BlobsetState; import udmi.schema.Category; import udmi.schema.Config; import udmi.schema.DevicePersistent; import udmi.schema.DiscoveryEvents; +import udmi.schema.DiscoveryUpload; import udmi.schema.EndpointConfiguration; import udmi.schema.Entry; import udmi.schema.Level; @@ -100,6 +105,7 @@ public interface PublisherHost extends ManagerHost { String UDMI_VERSION = SchemaVersion.CURRENT.key(); String BROKEN_VERSION = "1.4."; String DATA_URL_JSON_BASE64 = "data:application/json;base64,"; + String DOWNLOAD_URL_PREFIX = "download://"; int STATE_THROTTLE_MS = 2000; int DEFAULT_REPORT_SEC = 10; int FORCED_STATE_TIME_MS = 10000; @@ -122,6 +128,7 @@ public interface PublisherHost extends ManagerHost { .put(MqttPublisher.FakeTopic.class, getEventsSuffix("racoon")) .put(MqttPublisher.InjectedState.class, STATE_TOPIC) .put(DiscoveryEvents.class, getEventsSuffix("discovery")) + .put(DiscoveryUpload.class, getUploadSuffix("discovery")) .build(); Map INVALID_REPLACEMENTS = ImmutableMap.of( "events/blobset", "\"\"", @@ -258,6 +265,17 @@ default String extractConfigBlob(String blobName) { } BlobBlobsetConfig blobBlobsetConfig = blobs.get(blobName); if (blobBlobsetConfig != null && FINAL.equals(blobBlobsetConfig.phase)) { + if (blobBlobsetConfig.url != null + && blobBlobsetConfig.url.startsWith(DOWNLOAD_URL_PREFIX)) { + BlobBlobsetState blobState = ensureBlobsetState(blobName); + if (!Objects.equals(blobState.generation, blobBlobsetConfig.generation) + || !BlobPhase.FINAL.equals(blobState.phase)) { + blobState.phase = BlobPhase.APPLY; + blobState.generation = blobBlobsetConfig.generation; + markStateDirty(); + } + return null; + } return acquireBlobData(blobBlobsetConfig.url, blobBlobsetConfig.sha256); } return null; @@ -437,6 +455,10 @@ static String getEventsSuffix(String suffixSuffix) { return format("%s/%s", MqttDevice.EVENTS_TOPIC, suffixSuffix); } + static String getUploadSuffix(String suffixSuffix) { + return format("%s/%s", MqttDevice.UPLOAD_TOPIC, suffixSuffix); + } + /** * Augments a given {@code message} object with the current timestamp and version information. */ @@ -559,6 +581,7 @@ default void disconnectMqtt() { */ default void registerMessageHandlers() { getDeviceTarget().registerHandler(CONFIG_TOPIC, this::configHandler, Config.class); + getDeviceTarget().registerHandler(DOWNLOAD_TOPIC, this::downloadHandler, BlobsetDownload.class); String gatewayId = getGatewayId(getDeviceId()); if (isGatewayDevice()) { // In this case, this is the gateway so register the appropriate error handler directly. @@ -571,6 +594,39 @@ default void registerMessageHandlers() { } } + /** + * Handles incoming ephemeral {@link BlobsetDownload} payloads on {@code download/blobset}. + */ + default void downloadHandler(BlobsetDownload download) { + if (download == null || download.blobs == null) { + return; + } + withStateLock(() -> { + EndpointConfiguration endpoint = download.blobs.get(IOT_ENDPOINT_CONFIG.value()); + if (endpoint != null) { + BlobBlobsetConfig blobConfig = + catchToNull(() -> getDeviceConfig().blobset.blobs.get(IOT_ENDPOINT_CONFIG.value())); + if (blobConfig == null || blobConfig.url == null + || !blobConfig.url.startsWith(DOWNLOAD_URL_PREFIX)) { + warn("Ignoring unsolicited endpoint download"); + return; + } + String actualSha256 = GeneralUtils.sha256(stringifyTerse(endpoint)); + if (!isNotEmpty(blobConfig.sha256)) { + endpoint.error = "Missing expected blob sha256"; + } else if (!blobConfig.sha256.equals(actualSha256)) { + endpoint.error = "Blob data hash mismatch"; + } else if (endpoint.expiry != null && !endpoint.expiry.after(getNow())) { + endpoint.error = "Endpoint configuration expired"; + } + if (blobConfig.generation != null) { + endpoint.generation = blobConfig.generation; + } + setExtractedEndpoint(endpoint); + } + }); + } + /** * Get MqttDevice for given proxy. * @@ -628,11 +684,15 @@ default void publisherHandler(String type, String phase, Throwable cause, String String usePhase = isBadCategory() ? "apply" : phase; String category = type == null ? UDMI_PUBLISHER_LOG_CATEGORY - : format(SYSTEM_CATEGORY_FORMAT, type, usePhase); + : "download".equals(type) + ? format("blobset.blob.%s", usePhase) + : format(SYSTEM_CATEGORY_FORMAT, type, usePhase); Entry report = entryFromException(category, cause); getDeviceManager().localLog(report); publishLogMessage(report, targetId); - registerSystemStatus(report, targetId); + if (!"download".equals(type)) { + registerSystemStatus(report, targetId); + } } /** @@ -876,6 +936,14 @@ default void maybeTweakState() { * @return The extracted {@link EndpointConfiguration} */ default EndpointConfiguration extractEndpointBlobConfig() { + BlobBlobsetConfig blobConfig = + catchToNull(() -> getDeviceConfig().blobset.blobs.get(IOT_ENDPOINT_CONFIG.value())); + if (blobConfig != null && blobConfig.url != null + && blobConfig.url.startsWith(DOWNLOAD_URL_PREFIX) + && getExtractedEndpoint() != null + && Objects.equals(getExtractedEndpoint().generation, blobConfig.generation)) { + return getExtractedEndpoint(); + } setExtractedEndpoint(null); if (getDeviceConfig().blobset == null) { return null; @@ -942,6 +1010,12 @@ default void maybeRedirectEndpoint(EndpointConfiguration extractedEndpoint) { : redirectedEndpoint(redirectRegistry); if (extractedSignature == null) { + BlobBlobsetConfig pendingBlob = + catchToNull(() -> getDeviceConfig().blobset.blobs.get(IOT_ENDPOINT_CONFIG.value())); + if (pendingBlob != null && pendingBlob.url != null + && pendingBlob.url.startsWith(DOWNLOAD_URL_PREFIX)) { + return; + } setAttemptedEndpoint(null); removeBlobsetBlobState(IOT_ENDPOINT_CONFIG); return; @@ -951,6 +1025,14 @@ default void maybeRedirectEndpoint(EndpointConfiguration extractedEndpoint) { if (extractedSignature.equals(currentSignature) || extractedSignature.equals(getAttemptedEndpoint())) { + if (extractedEndpoint != null && extractedEndpoint.error == null + && endpointState.phase == BlobBlobsetConfig.BlobPhase.APPLY) { + endpointState.phase = BlobBlobsetConfig.BlobPhase.FINAL; + endpointState.status = null; + endpointState.generation = extractedEndpoint.generation; + persistEndpoint(extractedEndpoint); + markStateDirty(); + } return; // No need to redirect anything! } diff --git a/pubber/src/main/java/udmi/lib/client/manager/DiscoveryManager.java b/pubber/src/main/java/udmi/lib/client/manager/DiscoveryManager.java index 3f2ad302ee..632643916e 100644 --- a/pubber/src/main/java/udmi/lib/client/manager/DiscoveryManager.java +++ b/pubber/src/main/java/udmi/lib/client/manager/DiscoveryManager.java @@ -5,6 +5,7 @@ import static com.google.udmi.util.GeneralUtils.ifNullThen; import static com.google.udmi.util.GeneralUtils.ifTrueGet; import static com.google.udmi.util.GeneralUtils.ifTrueThen; +import static com.google.udmi.util.GeneralUtils.sha256; import static com.google.udmi.util.JsonUtil.getNowInstant; import static com.google.udmi.util.JsonUtil.isoConvert; import static java.lang.Math.floorMod; @@ -15,8 +16,10 @@ import static udmi.schema.FamilyDiscoveryState.Phase.STOPPED; import com.google.common.collect.ImmutableSet; +import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; +import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -32,6 +35,7 @@ import udmi.schema.DiscoveryConfig; import udmi.schema.DiscoveryEvents; import udmi.schema.DiscoveryState; +import udmi.schema.DiscoveryUpload; import udmi.schema.Enumerations; import udmi.schema.Enumerations.Depth; import udmi.schema.FamilyDiscoveryConfig; @@ -278,9 +282,17 @@ default void startDiscoveryScan(String family, Date scanGeneration) { familyDiscoveryState.generation = scanGeneration; familyDiscoveryState.phase = ACTIVE; familyDiscoveryState.active_count = sendCount.get(); - sendMarkerDiscoveryEvent(family, scanGeneration, sendCount); + if (!isTraceDepth(family)) { + sendMarkerDiscoveryEvent(family, scanGeneration, sendCount); + } updateState(); - startDiscoveryForFamily(family, scanGeneration, familyDiscoveryState, sendCount); + if (!isTraceDepth(family)) { + startDiscoveryForFamily(family, scanGeneration, familyDiscoveryState, sendCount); + } + } + + default boolean isTraceDepth(String family) { + return Depth.TRACE.equals(catchToNull(() -> getFamilyDiscoveryConfig(family).depth)); } private void sendMarkerDiscoveryEvent(String family, Date scanGeneration, @@ -292,6 +304,43 @@ private void sendMarkerDiscoveryEvent(String family, Date scanGeneration, publishDiscoveryEvent(family, scanGeneration, null, discoveryEvent); } + /** + * Publishes a discovery upload stream for trace-depth scans. + */ + default void publishTraceDiscoveryUploads(String family, Date scanGeneration, + FamilyDiscoveryState familyDiscoveryState) { + final List chunks = List.of( + "PCAP-HEADER-AND-FIRST-PACKET-".getBytes(StandardCharsets.UTF_8), + "SECOND-PACKET-AND-FOOTER".getBytes(StandardCharsets.UTF_8)); + final byte[] fullPayload = "PCAP-HEADER-AND-FIRST-PACKET-SECOND-PACKET-AND-FOOTER" + .getBytes(StandardCharsets.UTF_8); + final int totalChunks = chunks.size(); + + DiscoveryUpload startUpload = new DiscoveryUpload(); + startUpload.family = family; + startUpload.generation = scanGeneration; + startUpload.event_no = 0; + getHost().publish(startUpload); + + for (int idx = 0; idx < totalChunks; idx++) { + DiscoveryUpload chunkUpload = new DiscoveryUpload(); + chunkUpload.family = family; + chunkUpload.generation = scanGeneration; + chunkUpload.event_no = idx + 1; + chunkUpload.data = Base64.getEncoder().encodeToString(chunks.get(idx)); + getHost().publish(chunkUpload); + } + + DiscoveryUpload eofUpload = new DiscoveryUpload(); + eofUpload.family = family; + eofUpload.generation = scanGeneration; + eofUpload.event_no = -(totalChunks + 1); + eofUpload.sha256 = sha256(fullPayload); + getHost().publish(eofUpload); + + familyDiscoveryState.active_count = totalChunks; + } + /** * Starts a discovery scan for given family. */ @@ -379,11 +428,15 @@ default void discoveryScanComplete(String family, Date scanGeneration, AtomicInt info(format("Discovered %s stopping %s (=? %s)", family, isoConvert(scanGeneration), isoConvert(familyDiscoveryState.generation))); ifTrueThen(scanGeneration.equals(familyDiscoveryState.generation), () -> { - FamilyProvider provider = discoveryProvider(family); - if (provider != null) { - provider.stopScan(); + if (isTraceDepth(family)) { + publishTraceDiscoveryUploads(family, scanGeneration, familyDiscoveryState); + } else { + FamilyProvider provider = discoveryProvider(family); + if (provider != null) { + provider.stopScan(); + } + sendMarkerDiscoveryEvent(family, scanGeneration, sendCount); } - sendMarkerDiscoveryEvent(family, scanGeneration, sendCount); familyDiscoveryState.phase = STOPPED; updateState(); scheduleDiscoveryScan(family); diff --git a/pubber/src/test/java/daq/pubber/PubberTest.java b/pubber/src/test/java/daq/pubber/PubberTest.java index 42c505556b..20691d3179 100644 --- a/pubber/src/test/java/daq/pubber/PubberTest.java +++ b/pubber/src/test/java/daq/pubber/PubberTest.java @@ -290,4 +290,69 @@ public void initializePersistentStoreFromPersistentDataTest() { assertEquals(pubber.persistentData.endpoint.hostname, "persistent"); assertEquals(pubber.getPubberConfig().endpoint.hostname, "persistent"); } + + @Test + public void downloadEndpointFlowTest() { + pubber = makeTestPubber(TEST_DEVICE); + final EndpointConfiguration targetEndpoint = getEndpointConfiguration(TEST_REDIRECT_HOSTNAME); + final Date generation = new Date(); + BlobBlobsetConfig blobConfig = new BlobBlobsetConfig(); + blobConfig.url = "download://_iot_endpoint_config"; + blobConfig.sha256 = sha256(JsonUtil.stringifyTerse(targetEndpoint)); + blobConfig.phase = BlobPhase.FINAL; + blobConfig.generation = generation; + pubber.getDeviceConfig().blobset = new BlobsetConfig(); + pubber.getDeviceConfig().blobset.blobs = new HashMap<>(); + pubber.getDeviceConfig().blobset.blobs.put(IOT_ENDPOINT_CONFIG.value(), blobConfig); + + assertNull(pubber.extractEndpointBlobConfig()); + assertEquals(BlobPhase.APPLY, + pubber.getDeviceState().blobset.blobs.get(IOT_ENDPOINT_CONFIG.value()).phase); + + udmi.schema.BlobsetDownload download = new udmi.schema.BlobsetDownload(); + download.blobs = new HashMap<>(); + download.blobs.put(IOT_ENDPOINT_CONFIG.value(), targetEndpoint); + pubber.downloadHandler(download); + + assertNotEquals(null, pubber.getExtractedEndpoint()); + assertNull(pubber.getExtractedEndpoint().error); + assertEquals(generation, pubber.getExtractedEndpoint().generation); + + // Subsequent config extraction with same generation must preserve downloaded endpoint + assertNotEquals(null, pubber.extractEndpointBlobConfig()); + + pubber.maybeRedirectEndpoint(pubber.getExtractedEndpoint()); + assertEquals(BlobPhase.FINAL, + pubber.getDeviceState().blobset.blobs.get(IOT_ENDPOINT_CONFIG.value()).phase); + assertNull(pubber.getDeviceState().blobset.blobs.get(IOT_ENDPOINT_CONFIG.value()).status); + } + + @Test + public void downloadEndpointBadHashTest() { + pubber = makeTestPubber(TEST_DEVICE); + final EndpointConfiguration targetEndpoint = getEndpointConfiguration(TEST_REDIRECT_HOSTNAME); + final Date generation = new Date(); + BlobBlobsetConfig blobConfig = new BlobBlobsetConfig(); + blobConfig.url = "download://_iot_endpoint_config"; + blobConfig.sha256 = "0000000000000000000000000000000000000000000000000000000000000000"; + blobConfig.phase = BlobPhase.FINAL; + blobConfig.generation = generation; + pubber.getDeviceConfig().blobset = new BlobsetConfig(); + pubber.getDeviceConfig().blobset.blobs = new HashMap<>(); + pubber.getDeviceConfig().blobset.blobs.put(IOT_ENDPOINT_CONFIG.value(), blobConfig); + + assertNull(pubber.extractEndpointBlobConfig()); + + udmi.schema.BlobsetDownload download = new udmi.schema.BlobsetDownload(); + download.blobs = new HashMap<>(); + download.blobs.put(IOT_ENDPOINT_CONFIG.value(), targetEndpoint); + pubber.downloadHandler(download); + + assertEquals("Blob data hash mismatch", pubber.getExtractedEndpoint().error); + pubber.maybeRedirectEndpoint(pubber.getExtractedEndpoint()); + assertEquals(BlobPhase.FINAL, + pubber.getDeviceState().blobset.blobs.get(IOT_ENDPOINT_CONFIG.value()).phase); + assertEquals(udmi.schema.Category.BLOBSET_BLOB_APPLY, + pubber.getDeviceState().blobset.blobs.get(IOT_ENDPOINT_CONFIG.value()).status.category); + } } diff --git a/schema/category.json b/schema/category.json index d6ea72c022..54d9a76d53 100644 --- a/schema/category.json +++ b/schema/category.json @@ -1,7 +1,6 @@ { "$udmi_version": "1.5.7", "title": "Category", - "$schema": "http://json-schema.org/draft-07/schema#", "$comment": "Auto-generated category mappings from bin/gencode_categories.", "type": "string", "oneOf": [ diff --git a/schema/configuration_endpoint.json b/schema/configuration_endpoint.json index 1a255bd5cd..846236c790 100644 --- a/schema/configuration_endpoint.json +++ b/schema/configuration_endpoint.json @@ -257,6 +257,16 @@ "display": "show", "style": "bold" } + }, + "expiry": { + "description": "Timestamp when the provisioned credential or endpoint expires", + "type": "string", + "format": "date-time", + "examples": ["2019-01-17T14:02:29.364Z"], + "$presentation": { + "display": "show", + "style": "bold" + } } }, "required": [ diff --git a/schema/download_blobset.json b/schema/download_blobset.json new file mode 100644 index 0000000000..678a9745fc --- /dev/null +++ b/schema/download_blobset.json @@ -0,0 +1,37 @@ +{ + "$udmi_version": "1.5.7", + "title": "Blobset Download", + "description": "Direct cloud-to-device payload delivery for blobset configurations over the download/blobset channel.", + "$section": "Messages", + "type": "object", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "timestamp": { + "description": "RFC 3339 UTC timestamp the download payload was generated", + "type": "string", + "format": "date-time", + "examples": ["2019-01-17T14:02:29.364Z"] + }, + "version": { + "description": "Version of the UDMI schema", + "type": "string" + }, + "blobs": { + "description": "Map of system blobset keys to their provisioned endpoint configuration payloads", + "type": "object", + "additionalProperties": false, + "existingJavaType": "java.util.HashMap", + "patternProperties": { + "^_[a-z_]+$": { + "$ref": "file:configuration_endpoint.json#" + } + } + } + }, + "required": [ + "timestamp", + "version", + "blobs" + ] +} diff --git a/schema/envelope.json b/schema/envelope.json index 8dac2d7f1c..bf92562c1d 100644 --- a/schema/envelope.json +++ b/schema/envelope.json @@ -68,8 +68,7 @@ "pointset", "validation", "blobset", - "monitoring", - "streams" + "monitoring" ] }, "subType": { @@ -85,7 +84,9 @@ "query", "reply", "model", - "propose" + "propose", + "upload", + "download" ] } }, diff --git a/schema/events.json b/schema/events.json index 9ae8ec7de3..3d8d0e387a 100644 --- a/schema/events.json +++ b/schema/events.json @@ -14,9 +14,6 @@ }, "discovery": { "$ref": "file:events_discovery.json#" - }, - "streams": { - "$ref": "file:events_streams.json#" } } } diff --git a/schema/events_streams.json b/schema/events_streams.json deleted file mode 100644 index b9df2fcade..0000000000 --- a/schema/events_streams.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "$udmi_version": "1.5.7", - "title": "Streams Events", - "description": "Sequential data stream chunks for reliable transport over MQTT (e.g. PCAP traces, firmware blobs, reliable alarms, logs playback).", - "$section": "Messages", - "type": "object", - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "timestamp": { - "description": "RFC 3339 UTC timestamp the stream chunk event was generated", - "type": "string", - "format": "date-time" - }, - "version": { - "description": "Version of the UDMI schema", - "type": "string" - }, - "session_id": { - "description": "Unique session identifier for the continuous stream transmission", - "type": "string" - }, - "event_no": { - "description": "Sequence number for this stream event to support reliable sequential delivery and reassembly", - "type": "integer" - }, - "chunk_index": { - "description": "0-based index of the transmitted chunk payload within the total session", - "type": "integer" - }, - "total_chunks": { - "description": "Total number of chunks comprising the complete transmission binary", - "type": "integer" - }, - "data": { - "description": "Base64-encoded binary chunk payload data", - "type": "string" - } - } -} diff --git a/schema/upload_discovery.json b/schema/upload_discovery.json new file mode 100644 index 0000000000..82e8a64e65 --- /dev/null +++ b/schema/upload_discovery.json @@ -0,0 +1,51 @@ +{ + "$udmi_version": "1.5.7", + "title": "Discovery Upload", + "description": "Sequential binary chunk upload for discovery artifacts (e.g. PCAP traces) framed by config.discovery and state.discovery.", + "$section": "Messages", + "type": "object", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "timestamp": { + "description": "RFC 3339 UTC timestamp the upload chunk was generated", + "type": "string", + "format": "date-time", + "examples": ["2019-01-17T14:02:29.364Z"] + }, + "version": { + "description": "Version of the UDMI schema", + "type": "string" + }, + "family": { + "description": "Protocol family associated with the discovery trace upload", + "type": "string" + }, + "generation": { + "description": "The discovery scan trigger's generation timestamp", + "type": "string", + "format": "date-time", + "examples": ["2019-01-17T14:02:29.364Z"] + }, + "event_no": { + "description": "Sequence number for this upload chunk: 0 for start marker, 1..N for data chunks, -(N + 1) for terminal EOF marker", + "type": "integer" + }, + "sha256": { + "description": "SHA-256 hex digest of the complete unencoded binary payload (included on the terminal negative event_no marker)", + "type": "string", + "pattern": "^[0-9a-f]{64}$" + }, + "data": { + "description": "Base64-encoded binary chunk payload data", + "type": "string" + } + }, + "required": [ + "timestamp", + "version", + "family", + "generation", + "event_no" + ] +} diff --git a/tests/schemas/download_blobset/endpoint.json b/tests/schemas/download_blobset/endpoint.json new file mode 100644 index 0000000000..ef2066681f --- /dev/null +++ b/tests/schemas/download_blobset/endpoint.json @@ -0,0 +1,28 @@ +// +// Sent on MQTT topic: /devices/{device_id}/download/blobset +// Ephemeral cloud-to-device blobset delivery. +// +{ + "version": "1.5.7", + "timestamp": "2019-01-17T14:02:29.364Z", + "blobs": { + "_iot_endpoint_config": { + "protocol": "mqtt", + "transport": "ssl", + "hostname": "mqtt.example.com", + "port": 8883, + "client_id": "AHU-1", + "generation": "2019-01-17T14:02:00.000Z", + "expiry": "2019-01-18T14:02:00.000Z", + "auth_provider": { + "basic": { + "username": "AHU-1", + "password": "rotated-secret-password" + }, + "jwt": { + "audience": "example-project" + } + } + } + } +} diff --git a/tests/schemas/download_blobset/endpoint.out b/tests/schemas/download_blobset/endpoint.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/schemas/envelope/errors1.out b/tests/schemas/envelope/errors1.out index e47771c99a..b700b4be53 100644 --- a/tests/schemas/envelope/errors1.out +++ b/tests/schemas/envelope/errors1.out @@ -2,4 +2,4 @@ /deviceId: ECMA 262 regex "^[A-Z]{2,6}-[1-9][0-9]*$" does not match input string "fcu-1" /deviceNumId: ECMA 262 regex "^[0-9]+$" does not match input string "921302198324X" /deviceRegistryId: ECMA 262 regex "^[a-zA-Z][-a-zA-Z0-9._+~%]*[a-zA-Z0-9]$" does not match input string "test/registry" - /subType: instance value (5) not found in enum (possible values: ["invalid","local","reflect","events","errors","commands","config","state","query","reply","model","propose"]) + /subType: instance value (5) not found in enum (possible values: ["invalid","local","reflect","events","errors","commands","config","state","query","reply","model","propose","upload","download"]) diff --git a/tests/schemas/upload_discovery/chunk.json b/tests/schemas/upload_discovery/chunk.json new file mode 100644 index 0000000000..59b05ad4d6 --- /dev/null +++ b/tests/schemas/upload_discovery/chunk.json @@ -0,0 +1,12 @@ +// +// Sent on MQTT topic: /devices/{device_id}/upload/discovery +// Sequential binary chunk upload for a discovery PCAP trace. +// +{ + "version": "1.5.7", + "timestamp": "2019-01-17T14:02:29.364Z", + "family": "bacnet", + "generation": "2019-01-17T14:02:00.000Z", + "event_no": 1, + "data": "dWRtaS1wY2FwLWNodW5rLWRhdGE=" +} diff --git a/tests/schemas/upload_discovery/chunk.out b/tests/schemas/upload_discovery/chunk.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/schemas/upload_discovery/eof.json b/tests/schemas/upload_discovery/eof.json new file mode 100644 index 0000000000..bfadb34ecd --- /dev/null +++ b/tests/schemas/upload_discovery/eof.json @@ -0,0 +1,11 @@ +// +// Terminal EOF boundary marker on /devices/{device_id}/upload/discovery +// +{ + "version": "1.5.7", + "timestamp": "2019-01-17T14:02:31.364Z", + "family": "bacnet", + "generation": "2019-01-17T14:02:00.000Z", + "event_no": -3, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" +} diff --git a/tests/schemas/upload_discovery/eof.out b/tests/schemas/upload_discovery/eof.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/udmis/etc/local_pod.json b/udmis/etc/local_pod.json index 10b6581621..9f2e827267 100644 --- a/udmis/etc/local_pod.json +++ b/udmis/etc/local_pod.json @@ -22,6 +22,9 @@ "target": { "recv_id": "events/#" }, + "upload": { + "recv_id": "upload/#" + }, "state": { "recv_id": "state" }, @@ -46,6 +49,10 @@ "uufi_events": { "recv_id": "events/#", "send_id": "/uufi/#" + }, + "uufi_upload": { + "recv_id": "upload/#", + "send_id": "/uufi/#" } }, "crons": { diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java index c1c74a3133..77f5231ded 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/DynamicIotAccessProvider.java @@ -215,6 +215,25 @@ public void sendCommandBase(Envelope envelope, SubFolder folder, getProviderFor(envelope).sendCommandBase(envelope, folder, message); } + @Override + public void sendDownloadBase(Envelope envelope, SubFolder folder, String message) { + getProviderFor(envelope).sendDownloadBase(envelope, folder, message); + } + + @Override + public void provisionDownloadBlob(String registryId, String deviceId, String blobKey, + udmi.schema.EndpointConfiguration endpoint) { + getRegistryProvider(registryId, deviceId) + .provisionDownloadBlob(registryId, deviceId, blobKey, endpoint); + } + + @Override + public udmi.schema.EndpointConfiguration fetchDownloadBlob(String registryId, String deviceId, + String blobKey) { + return getRegistryProvider(registryId, deviceId) + .fetchDownloadBlob(registryId, deviceId, blobKey); + } + /** * Sets the provider affinity for a given registry and device. * diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java index deb758f3ef..5b68844afd 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessBase.java @@ -309,6 +309,45 @@ public void setProviderAffinity(String registryId, String deviceId, String provi registryBackoffClear(registryId, deviceId); } + /** + * Send an ephemeral download message directly to a device. + */ + public final void sendDownload(Envelope envelope, SubFolder folder, String message) { + String registryId = requireNonNull(envelope.deviceRegistryId, "registry not defined"); + String deviceId = requireNonNull(envelope.deviceId, "device not defined"); + debug("Sending download to %s/%s/%s", registryId, deviceId, folder); + sendDownloadBase(envelope, folder, message); + } + + @Override + public void handleBlobsetState(Envelope envelope, udmi.schema.BlobsetState blobsetState) { + if (blobsetState == null || blobsetState.blobs == null || blobsetState.blobs.isEmpty()) { + return; + } + String registryId = envelope.deviceRegistryId; + String deviceId = envelope.deviceId; + if (registryId == null || deviceId == null) { + return; + } + java.util.HashMap matchedBlobs = + new java.util.HashMap<>(); + blobsetState.blobs.forEach((key, blobState) -> { + if (blobState != null && blobState.phase == udmi.schema.BlobBlobsetConfig.BlobPhase.APPLY) { + udmi.schema.EndpointConfiguration endpoint = fetchDownloadBlob(registryId, deviceId, key); + if (endpoint != null) { + matchedBlobs.put(key, endpoint); + } + } + }); + if (!matchedBlobs.isEmpty()) { + udmi.schema.BlobsetDownload download = new udmi.schema.BlobsetDownload(); + download.version = UdmiServicePod.UDMI_VERSION; + download.timestamp = new java.util.Date(); + download.blobs = matchedBlobs; + sendDownload(envelope, SubFolder.BLOBSET, com.google.udmi.util.JsonUtil.stringify(download)); + } + } + /** * Update the cached registry regions with any incremental updates. */ diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java index 21a7e4de3d..8c241c25dc 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/IotAccessProvider.java @@ -74,6 +74,21 @@ CloudModel modelDevice(String registryId, String deviceId, void sendCommandBase(Envelope envelope, SubFolder folder, String message); + default void sendDownloadBase(Envelope envelope, SubFolder folder, String message) { + } + + default void provisionDownloadBlob(String registryId, String deviceId, String blobKey, + udmi.schema.EndpointConfiguration endpoint) { + } + + default udmi.schema.EndpointConfiguration fetchDownloadBlob(String registryId, String deviceId, + String blobKey) { + return null; + } + + default void handleBlobsetState(Envelope envelope, udmi.schema.BlobsetState blobsetState) { + } + /** * Returns true if this provider supports registry and device management operations. */ diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java index 4d670b7825..abfd2cf681 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/LocalIotAccessProvider.java @@ -104,6 +104,13 @@ public void sendCommandBase(Envelope envelope, SubFolder folder, String message) format("%s/%s/%s:%s", envelope.deviceRegistryId, envelope.deviceId, folder, message)); } + @Override + public void sendDownloadBase(Envelope envelope, SubFolder folder, String message) { + sentCommands.add( + format("download:%s/%s/%s:%s", + envelope.deviceRegistryId, envelope.deviceId, folder, message)); + } + @TestOnly public void setFailureForTest() { failActivation = true; diff --git a/udmis/src/main/java/com/google/bos/udmi/service/access/ZanzaraIotAccessProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/access/ZanzaraIotAccessProvider.java index ee406c6c80..ba8e3eb7c2 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/access/ZanzaraIotAccessProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/access/ZanzaraIotAccessProvider.java @@ -922,6 +922,49 @@ public void sendCommandBase(Envelope baseEnvelope, SubFolder folder, String mess } } + @Override + public void sendDownloadBase(Envelope baseEnvelope, SubFolder folder, String message) { + if (mqttPipe == null) { + warn("MQTT pipe not initialized, unable to send download"); + return; + } + try { + Envelope envelope = deepCopy(baseEnvelope); + envelope.gatewayId = null; + envelope.subFolder = folder; + envelope.subType = SubType.DOWNLOAD; + envelope.source = IotProvider.ZANZARA.value(); + + Bundle bundle = new Bundle(envelope, MessageDispatcher.rawString(message)); + mqttPipe.publish(bundle); + debug("Published download to pipe for %s/%s/%s", + baseEnvelope.deviceRegistryId, baseEnvelope.deviceId, folder); + } catch (Exception e) { + error("Failed to send download for %s/%s: %s", + baseEnvelope.deviceRegistryId, baseEnvelope.deviceId, friendlyStackTrace(e)); + throw new RuntimeException("While sending download for " + + baseEnvelope.deviceRegistryId + "/" + baseEnvelope.deviceId, e); + } + } + + @Override + public void provisionDownloadBlob(String registryId, String deviceId, String blobKey, + EndpointConfiguration endpoint) { + requireNonNull(registryId, "registryId not defined"); + requireNonNull(deviceId, "deviceId not defined"); + requireNonNull(blobKey, "blobKey not defined"); + requireNonNull(endpoint, "endpoint not defined"); + registryDeviceRef(registryId, deviceId) + .put("download_blob_" + blobKey, stringifyTerse(endpoint)); + } + + @Override + public EndpointConfiguration fetchDownloadBlob(String registryId, String deviceId, + String blobKey) { + String json = registryDeviceRef(registryId, deviceId).get("download_blob_" + blobKey); + return json == null ? null : JsonUtil.fromString(EndpointConfiguration.class, json); + } + @Override public void shutdown() { ifNotNullThen(mqttPipe, pipe -> { diff --git a/udmis/src/main/java/com/google/bos/udmi/service/core/ProcessorBase.java b/udmis/src/main/java/com/google/bos/udmi/service/core/ProcessorBase.java index 319967b984..00f2baf872 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/core/ProcessorBase.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/core/ProcessorBase.java @@ -171,6 +171,28 @@ protected String processConfigChange(Envelope envelope, Object payload, Date new return configUpdate; } + protected void processDownloadChange(Envelope envelope, Object payload) { + if (envelope.subFolder != SubFolder.BLOBSET || payload == null || iotAccess == null) { + return; + } + udmi.schema.BlobsetDownload download = + com.google.udmi.util.JsonUtil.convertTo(udmi.schema.BlobsetDownload.class, payload); + if (download == null || download.blobs == null) { + return; + } + String registryId = envelope.deviceRegistryId; + String deviceId = envelope.deviceId; + download.blobs.forEach( + (key, endpoint) -> iotAccess.provisionDownloadBlob(registryId, deviceId, key, endpoint)); + String currentState = iotAccess.fetchState(registryId, deviceId); + if (isNotEmpty(currentState)) { + StateUpdate stateUpdate = fromJsonString(currentState, StateUpdate.class); + if (stateUpdate != null && stateUpdate.blobset != null) { + iotAccess.handleBlobsetState(envelope, stateUpdate.blobset); + } + } + } + protected void reflectError(SubType subType, BundleException bundleException) { Bundle bundle = bundleException.bundle; Map errorMap = bundle.attributesMap; diff --git a/udmis/src/main/java/com/google/bos/udmi/service/core/ReflectProcessor.java b/udmis/src/main/java/com/google/bos/udmi/service/core/ReflectProcessor.java index 84c3e0f9e5..a9040da863 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/core/ReflectProcessor.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/core/ReflectProcessor.java @@ -363,6 +363,9 @@ private CloudModel reflectProcess(Envelope attributes, Object payload) { // If there was actually a config change, then the transaction will be acknowledged that way. ifNotNullThen(processConfigChange(attributes, payload, null), updated -> attributes.transactionId = makeConfigUpdateTransaction(attributes.transactionId)); + } else if (attributes.subType == SubType.DOWNLOAD) { + processDownloadChange(attributes, payload); + return reply; } if (payload instanceof String) { return reply; diff --git a/udmis/src/main/java/com/google/bos/udmi/service/core/StateProcessor.java b/udmis/src/main/java/com/google/bos/udmi/service/core/StateProcessor.java index 2e6bddb183..3ee5f0d42b 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/core/StateProcessor.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/core/StateProcessor.java @@ -63,6 +63,7 @@ protected SubType getExceptionSubType() { private void processStateUpdate(MessageContinuation continuation, Envelope envelope, StateUpdate message) { iotAccess.saveState(envelope.deviceRegistryId, envelope.deviceId, stringifyTerse(message)); + ifNotNullThen(message.blobset, blobset -> iotAccess.handleBlobsetState(envelope, blobset)); continuation.publish(message); String origTxnId = envelope.transactionId; AtomicInteger txnSuffix = new AtomicInteger(); diff --git a/udmis/src/main/java/com/google/bos/udmi/service/core/TargetProcessor.java b/udmis/src/main/java/com/google/bos/udmi/service/core/TargetProcessor.java index 04556a85c0..76cbc3a1f8 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/core/TargetProcessor.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/core/TargetProcessor.java @@ -68,6 +68,36 @@ protected void defaultHandler(Object defaultedMessage) { reflectMessage(envelope, message); } + /** + * Handle chunked discovery uploads arriving on the target message channel. + */ + @MessageHandler + public void discoveryUploadHandler(udmi.schema.DiscoveryUpload message) { + MessageContinuation continuation = getContinuation(message); + Envelope envelope = continuation.getEnvelope(); + defaultFields(message); + publish(message); + if (envelope.deviceId == null) { + notice("Dropping message with no deviceId: " + stringifyTerse(envelope)); + return; + } + if (envelope.subType == null) { + envelope.subType = SubType.UPLOAD; + } + if (envelope.subFolder == null) { + envelope.subFolder = SubFolder.DISCOVERY; + } + updateLastSeen(envelope); + UploadProcessor uploadProcessor = + com.google.bos.udmi.service.pod.UdmiServicePod.maybeGetComponent(UploadProcessor.class); + if (uploadProcessor != null) { + uploadProcessor.handleDiscoveryUpload(envelope, message); + } else { + reflectMessage(envelope, stringify(message)); + UploadProcessor.processDiscoveryUpload(envelope, message); + } + } + @Override protected SubType getExceptionSubType() { return SubType.EVENTS; diff --git a/udmis/src/main/java/com/google/bos/udmi/service/core/UploadProcessor.java b/udmis/src/main/java/com/google/bos/udmi/service/core/UploadProcessor.java new file mode 100644 index 0000000000..5be4c60740 --- /dev/null +++ b/udmis/src/main/java/com/google/bos/udmi/service/core/UploadProcessor.java @@ -0,0 +1,233 @@ +package com.google.bos.udmi.service.core; + +import static com.google.udmi.util.JsonUtil.isoConvert; +import static com.google.udmi.util.JsonUtil.stringify; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +import com.google.bos.udmi.service.messaging.MessageContinuation; +import com.google.bos.udmi.service.pod.UdmiServicePod; +import com.google.bos.udmi.service.support.DataRef; +import com.google.bos.udmi.service.support.IotDataProvider; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.util.Base64; +import java.util.HashMap; +import java.util.HexFormat; +import java.util.Map; +import java.util.Set; +import udmi.schema.DiscoveryUpload; +import udmi.schema.EndpointConfiguration; +import udmi.schema.Envelope; +import udmi.schema.Envelope.SubFolder; +import udmi.schema.Envelope.SubType; + +/** + * Processor for device-to-cloud chunked binary uploads over the {@code upload} channel. + * + *

Stages incoming chunks in the shared {@link IotDataProvider} (Etcd) keyed by + * {@code (family, generation)} so multiple UDMIS pods can receive chunks in any order and uses an + * atomic Compare-And-Swap election to ensure exactly one pod reassembles and verifies the complete + * payload once the start marker, all data chunks, and the negative EOF boundary marker have + * arrived. + */ +@ComponentName("upload") +public class UploadProcessor extends ProcessorBase { + + public static final String DATABASE_COMPONENT = "database"; + public static final String STARTED_KEY = "started"; + public static final String CHUNK_KEY_PREFIX = "chunk_"; + public static final String EXPECTED_CHUNKS_KEY = "expected_chunks"; + public static final String EXPECTED_SHA256_KEY = "expected_sha256"; + public static final String STATUS_KEY = "status"; + public static final String STATUS_ASSEMBLING = "assembling"; + private static final String DEFAULT_UPLOAD_DIR = "/tmp/udmi_uploads"; + + private IotDataProvider database; + private File outputDir = new File(DEFAULT_UPLOAD_DIR); + + public UploadProcessor(EndpointConfiguration config) { + super(config); + } + + @Override + public void activate() { + database = UdmiServicePod.maybeGetComponent(DATABASE_COMPONENT); + super.activate(); + } + + @Override + protected SubType getExceptionSubType() { + return SubType.UPLOAD; + } + + public void setOutputDir(File outputDir) { + this.outputDir = requireNonNull(outputDir, "outputDir not defined"); + } + + public File getOutputDir() { + return outputDir; + } + + /** + * Handle discovery upload chunk messages on {@code upload/discovery}. + */ + @MessageHandler + public void discoveryUploadHandler(DiscoveryUpload message) { + MessageContinuation continuation = getContinuation(message); + handleDiscoveryUpload(continuation.getEnvelope(), message); + } + + /** + * Reflect and stage a discovery upload message using this processor's configured resources. + */ + public void handleDiscoveryUpload(Envelope envelope, DiscoveryUpload message) { + if (envelope.subType == null) { + envelope.subType = SubType.UPLOAD; + } + if (envelope.subFolder == null) { + envelope.subFolder = SubFolder.DISCOVERY; + } + reflectMessage(envelope, stringify(message)); + IotDataProvider dataProvider = database != null + ? database + : UdmiServicePod.maybeGetComponent(DATABASE_COMPONENT); + if (dataProvider != null && envelope.deviceId != null && envelope.deviceRegistryId != null) { + File outputFile = processDiscoveryUpload(envelope, message, dataProvider, outputDir); + if (outputFile != null) { + info("Reassembled discovery upload for %s/%s to %s", + envelope.deviceRegistryId, envelope.deviceId, outputFile.getAbsolutePath()); + } + } + } + + /** + * Stage a discovery upload chunk or boundary marker using the active pod's database and output + * directory. + */ + public static File processDiscoveryUpload(Envelope envelope, DiscoveryUpload message) { + IotDataProvider dataProvider = UdmiServicePod.maybeGetComponent(DATABASE_COMPONENT); + if (dataProvider == null || envelope.deviceId == null || envelope.deviceRegistryId == null) { + return null; + } + UploadProcessor uploadProcessor = UdmiServicePod.maybeGetComponent(UploadProcessor.class); + File outDir = uploadProcessor != null + ? uploadProcessor.getOutputDir() + : new File(DEFAULT_UPLOAD_DIR); + return processDiscoveryUpload(envelope, message, dataProvider, outDir); + } + + /** + * Stage a discovery upload chunk or boundary marker in shared {@link IotDataProvider} and + * atomically reassemble the binary artifact once all chunks and the EOF marker are present. + */ + public static File processDiscoveryUpload( + Envelope envelope, + DiscoveryUpload message, + IotDataProvider dataProvider, + File baseOutputDir) { + String registryId = requireNonNull(envelope.deviceRegistryId, "missing deviceRegistryId"); + String deviceId = requireNonNull(envelope.deviceId, "missing deviceId"); + String family = requireNonNull(message.family, "missing family"); + String generation = requireNonNull(isoConvert(message.generation), "missing generation"); + Integer eventNo = requireNonNull(message.event_no, "missing event_no"); + + String uploadKey = format("%s_%s", family, generation).replaceAll("[^a-zA-Z0-9._-]", "_"); + DataRef uploadRef = dataProvider.ref() + .registry(registryId) + .device(deviceId) + .collection("upload_" + uploadKey); + + if (eventNo == 0) { + Map startPuts = new HashMap<>(); + startPuts.put(STARTED_KEY, "true"); + startPuts.put("family", family); + startPuts.put("generation", generation); + uploadRef.update(startPuts, null); + } else if (eventNo > 0) { + if (message.data == null || message.data.isEmpty()) { + throw new IllegalArgumentException( + format("Upload chunk event_no %d for %s missing data payload", eventNo, uploadKey)); + } + Map chunkPuts = new HashMap<>(); + chunkPuts.put(CHUNK_KEY_PREFIX + eventNo, message.data); + uploadRef.update(chunkPuts, null); + } else { + int expectedChunks = -(eventNo + 1); + if (expectedChunks <= 0) { + throw new IllegalArgumentException( + format("Invalid terminal event_no %d for %s", eventNo, uploadKey)); + } + if (message.sha256 == null || message.sha256.isEmpty()) { + throw new IllegalArgumentException( + format("Terminal upload marker event_no %d for %s missing sha256", + eventNo, uploadKey)); + } + Map eofPuts = new HashMap<>(); + eofPuts.put(EXPECTED_CHUNKS_KEY, String.valueOf(expectedChunks)); + eofPuts.put(EXPECTED_SHA256_KEY, message.sha256); + uploadRef.update(eofPuts, null); + } + + Map entries = uploadRef.entries(); + String started = entries.get(STARTED_KEY); + String expectedChunksStr = entries.get(EXPECTED_CHUNKS_KEY); + String expectedSha256 = entries.get(EXPECTED_SHA256_KEY); + if (started == null || expectedChunksStr == null || expectedSha256 == null) { + return null; + } + + int totalChunks = Integer.parseInt(expectedChunksStr); + for (int i = 1; i <= totalChunks; i++) { + if (!entries.containsKey(CHUNK_KEY_PREFIX + i)) { + return null; + } + } + + if (!uploadRef.updateIfMatch(STATUS_KEY, null, Map.of(STATUS_KEY, STATUS_ASSEMBLING), null)) { + return null; + } + + Set cleanupKeys = uploadRef.entries().keySet(); + try { + ByteArrayOutputStream assembled = new ByteArrayOutputStream(); + for (int i = 1; i <= totalChunks; i++) { + String chunkBase64 = entries.get(CHUNK_KEY_PREFIX + i); + byte[] decoded = Base64.getDecoder().decode(chunkBase64); + assembled.write(decoded); + } + byte[] payloadBytes = assembled.toByteArray(); + String actualSha256 = computeSha256Hex(payloadBytes); + if (!expectedSha256.equalsIgnoreCase(actualSha256)) { + throw new IllegalStateException(format( + "SHA-256 mismatch for upload %s: expected %s, got %s", + uploadKey, expectedSha256, actualSha256)); + } + + File deviceOutputDir = new File(new File(baseOutputDir, registryId), deviceId); + Files.createDirectories(deviceOutputDir.toPath()); + File outputFile = new File(deviceOutputDir, uploadKey + ".pcap"); + Files.write(outputFile.toPath(), payloadBytes); + return outputFile; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException( + format("While reassembling upload %s for %s/%s", + uploadKey, registryId, deviceId), e); + } finally { + uploadRef.update(null, cleanupKeys); + } + } + + static String computeSha256Hex(byte[] data) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + return HexFormat.of().formatHex(digest.digest(data)); + } catch (Exception e) { + throw new RuntimeException("Unable to compute SHA-256 digest", e); + } + } +} diff --git a/udmis/src/main/java/com/google/bos/udmi/service/core/UufiProcessor.java b/udmis/src/main/java/com/google/bos/udmi/service/core/UufiProcessor.java index dbae389406..65d692ff05 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/core/UufiProcessor.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/core/UufiProcessor.java @@ -172,6 +172,9 @@ private void handleUufiInbound(Envelope envelope, Map messageMap publish(innerEnvelope, com.google.udmi.util.JsonUtil.toMap(configUpdate)); } return; + } else if (innerEnvelope.subType == SubType.DOWNLOAD) { + processDownloadChange(innerEnvelope, innerPayload); + return; } publish(innerEnvelope, innerPayload); diff --git a/udmis/src/main/java/com/google/bos/udmi/service/messaging/impl/SimpleMqttPipe.java b/udmis/src/main/java/com/google/bos/udmi/service/messaging/impl/SimpleMqttPipe.java index d65f96d5dc..e64ad177f7 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/messaging/impl/SimpleMqttPipe.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/messaging/impl/SimpleMqttPipe.java @@ -349,10 +349,11 @@ private String makeBrokerUrl(EndpointConfiguration endpoint) { return format(BROKER_URL_FORMAT, transport, endpoint.hostname, port); } - private MqttMessage makeMqttMessage(Bundle bundle) { + static MqttMessage makeMqttMessage(Bundle bundle) { MqttMessage message = new MqttMessage(); message.setPayload(bundle.sendBytes()); message.setRetained(shouldRetainMessage(bundle)); + message.setQos(getMessageQos(bundle)); return message; } @@ -404,8 +405,12 @@ private String extractTransactionId(String payload) { } } - private boolean shouldRetainMessage(Bundle bundle) { - return bundle.envelope.subType == SubType.CONFIG; + static int getMessageQos(Bundle bundle) { + return (bundle.envelope != null && bundle.envelope.subType == SubType.DOWNLOAD) ? 0 : 1; + } + + static boolean shouldRetainMessage(Bundle bundle) { + return bundle.envelope != null && bundle.envelope.subType == SubType.CONFIG; } private void subscribeToMessages() { @@ -419,7 +424,8 @@ private void subscribeToMessages() { boolean connected = mqttClient.isConnected(); trace("Subscribing %s, active=%s connected=%s", clientId, isActive(), connected); if (isActive() && connected) { - mqttClient.subscribe(subscribeTopic); + int qos = recvId.startsWith("download") || subscribeTopic.contains("/download") ? 0 : 1; + mqttClient.subscribe(subscribeTopic, qos); info("Subscribed %s to topic %s", clientId, subscribeTopic); } } diff --git a/udmis/src/main/java/com/google/bos/udmi/service/pod/UdmiServicePod.java b/udmis/src/main/java/com/google/bos/udmi/service/pod/UdmiServicePod.java index ec443eb507..a12cdddf87 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/pod/UdmiServicePod.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/pod/UdmiServicePod.java @@ -24,6 +24,7 @@ import com.google.bos.udmi.service.core.ReflectProcessor; import com.google.bos.udmi.service.core.StateProcessor; import com.google.bos.udmi.service.core.TargetProcessor; +import com.google.bos.udmi.service.core.UploadProcessor; import com.google.bos.udmi.service.core.UufiProcessor; import com.google.bos.udmi.service.support.IotDataProvider; import com.google.common.collect.ImmutableSet; @@ -61,7 +62,7 @@ public class UdmiServicePod extends ContainerBase { private static final Map COMPONENT_MAP = new ConcurrentHashMap<>(); private static final Set> PROCESSOR_CLASSES = ImmutableSet.of( TargetProcessor.class, ReflectProcessor.class, StateProcessor.class, ControlProcessor.class, - BitboxAdapter.class, DistributorPipe.class, UufiProcessor.class); + BitboxAdapter.class, DistributorPipe.class, UufiProcessor.class, UploadProcessor.class); private static final Map> PROCESSORS = new HashMap<>(); static { @@ -69,6 +70,7 @@ public class UdmiServicePod extends ContainerBase { PROCESSORS.put("uufi_out", UufiProcessor.class); PROCESSORS.put("uufi_state", UufiProcessor.class); PROCESSORS.put("uufi_events", UufiProcessor.class); + PROCESSORS.put("uufi_upload", UufiProcessor.class); } /** diff --git a/udmis/src/main/java/com/google/bos/udmi/service/support/EtcdDataProvider.java b/udmis/src/main/java/com/google/bos/udmi/service/support/EtcdDataProvider.java index 1667cf9538..4cef996253 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/support/EtcdDataProvider.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/support/EtcdDataProvider.java @@ -362,9 +362,13 @@ public DataRef ref() { public void shutdown() { try { if (enabled) { - scheduledExecutorService.shutdown(); - scheduledExecutorService.awaitTermination(HEARTBEAT_SEC, TimeUnit.SECONDS); - client.close(); + if (scheduledExecutorService != null) { + scheduledExecutorService.shutdown(); + scheduledExecutorService.awaitTermination(HEARTBEAT_SEC, TimeUnit.SECONDS); + } + if (client != null) { + client.close(); + } } } catch (Exception e) { throw new RuntimeException("While shutting down", e); diff --git a/udmis/src/main/java/com/google/bos/udmi/service/support/MosquittoBroker.java b/udmis/src/main/java/com/google/bos/udmi/service/support/MosquittoBroker.java index 880324dbcc..369365aa18 100644 --- a/udmis/src/main/java/com/google/bos/udmi/service/support/MosquittoBroker.java +++ b/udmis/src/main/java/com/google/bos/udmi/service/support/MosquittoBroker.java @@ -247,15 +247,19 @@ private CompletableFuture provisionClient(String clientId, String clientPa CompletableFuture a3 = addRoleAcl(roleName, "subscribePattern", clientId + "/errors", true); CompletableFuture a4 = - addRoleAcl(roleName, "publishClientSend", clientId + "/events/#", true); + addRoleAcl(roleName, "subscribePattern", clientId + "/download/#", true); CompletableFuture a5 = + addRoleAcl(roleName, "publishClientSend", clientId + "/events/#", true); + CompletableFuture a6 = + addRoleAcl(roleName, "publishClientSend", clientId + "/upload/#", true); + CompletableFuture a7 = addRoleAcl(roleName, "publishClientSend", clientId + "/state", true); if (isReflectRegistry(clientId)) { - CompletableFuture a6 = + CompletableFuture a8 = addRoleAcl(roleName, "publishClientSend", clientId + "/reflect", true); - return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6); + return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6, a7, a8); } - return CompletableFuture.allOf(a1, a2, a3, a4, a5); + return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6, a7); }).thenRun(() -> info("Device %s registered correctly.", clientId)); } } @@ -477,19 +481,23 @@ public CompletableFuture bindGateway(String gatewayId, String deviceId) { CompletableFuture a3 = addRoleAcl(roleName, "subscribePattern", deviceId + "/errors", true); CompletableFuture a4 = - addRoleAcl(roleName, "publishClientSend", deviceId + "/events/#", true); + addRoleAcl(roleName, "subscribePattern", deviceId + "/download/#", true); CompletableFuture a5 = - addRoleAcl(roleName, "publishClientSend", deviceId + "/state", true); + addRoleAcl(roleName, "publishClientSend", deviceId + "/events/#", true); CompletableFuture a6 = + addRoleAcl(roleName, "publishClientSend", deviceId + "/upload/#", true); + CompletableFuture a7 = + addRoleAcl(roleName, "publishClientSend", deviceId + "/state", true); + CompletableFuture a8 = addRoleAcl(roleName, "publishClientSend", deviceId + "/attach", true); if (isReflectRegistry(deviceId)) { - CompletableFuture a7 = + CompletableFuture a9 = addRoleAcl(roleName, "publishClientSend", deviceId + "/reflect", true); - return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6, a7); + return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6, a7, a8, a9); } - return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6); + return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6, a7, a8); } @Override @@ -508,19 +516,23 @@ public CompletableFuture unbindGateway(String gatewayId, String deviceId) CompletableFuture a3 = removeRoleAcl(roleName, "subscribePattern", deviceId + "/errors"); CompletableFuture a4 = - removeRoleAcl(roleName, "publishClientSend", deviceId + "/events/#"); + removeRoleAcl(roleName, "subscribePattern", deviceId + "/download/#"); CompletableFuture a5 = - removeRoleAcl(roleName, "publishClientSend", deviceId + "/state"); + removeRoleAcl(roleName, "publishClientSend", deviceId + "/events/#"); CompletableFuture a6 = + removeRoleAcl(roleName, "publishClientSend", deviceId + "/upload/#"); + CompletableFuture a7 = + removeRoleAcl(roleName, "publishClientSend", deviceId + "/state"); + CompletableFuture a8 = removeRoleAcl(roleName, "publishClientSend", deviceId + "/attach"); if (isReflectRegistry(deviceId)) { - CompletableFuture a7 = + CompletableFuture a9 = removeRoleAcl(roleName, "publishClientSend", deviceId + "/reflect"); - return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6, a7); + return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6, a7, a8, a9); } - return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6); + return CompletableFuture.allOf(a1, a2, a3, a4, a5, a6, a7, a8); } private CompletableFuture removeRoleAcl(String roleName, String type, String pattern) { diff --git a/udmis/src/test/java/com/google/bos/udmi/service/access/ZanzaraIotAccessProviderTest.java b/udmis/src/test/java/com/google/bos/udmi/service/access/ZanzaraIotAccessProviderTest.java index 032b7a78f8..418f281561 100644 --- a/udmis/src/test/java/com/google/bos/udmi/service/access/ZanzaraIotAccessProviderTest.java +++ b/udmis/src/test/java/com/google/bos/udmi/service/access/ZanzaraIotAccessProviderTest.java @@ -354,6 +354,45 @@ void testModelDeviceAddsRegistryToGetRegistries() { assertTrue(registries.contains(TEST_REGISTRY)); } + @Test + void testHandleBlobsetStatePushesDownload() throws Exception { + com.google.bos.udmi.service.messaging.impl.SimpleMqttPipe mockPipe = + mock(com.google.bos.udmi.service.messaging.impl.SimpleMqttPipe.class); + Field pipeField = ZanzaraIotAccessProvider.class.getDeclaredField("mqttPipe"); + pipeField.setAccessible(true); + pipeField.set(provider, mockPipe); + + udmi.schema.EndpointConfiguration provisionedEndpoint = new udmi.schema.EndpointConfiguration(); + provisionedEndpoint.protocol = udmi.schema.EndpointConfiguration.Protocol.MQTT; + provisionedEndpoint.hostname = "mqtt.example.com"; + provisionedEndpoint.client_id = TEST_DEVICE; + provider.provisionDownloadBlob( + TEST_REGISTRY, TEST_DEVICE, "_iot_endpoint_config", provisionedEndpoint); + + udmi.schema.BlobsetState blobsetState = new udmi.schema.BlobsetState(); + blobsetState.blobs = new HashMap<>(); + udmi.schema.BlobBlobsetState blobState = new udmi.schema.BlobBlobsetState(); + blobState.phase = udmi.schema.BlobBlobsetConfig.BlobPhase.APPLY; + blobsetState.blobs.put("_iot_endpoint_config", blobState); + + udmi.schema.Envelope env = new udmi.schema.Envelope(); + env.deviceRegistryId = TEST_REGISTRY; + env.deviceId = TEST_DEVICE; + env.gatewayId = "uufi"; + + provider.handleBlobsetState(env, blobsetState); + + org.mockito.ArgumentCaptor + bundleCaptor = org.mockito.ArgumentCaptor.forClass( + com.google.bos.udmi.service.messaging.impl.MessageBase.Bundle.class); + verify(mockPipe, times(1)).publish(bundleCaptor.capture()); + com.google.bos.udmi.service.messaging.impl.MessageBase.Bundle published = + bundleCaptor.getValue(); + assertEquals(udmi.schema.Envelope.SubType.DOWNLOAD, published.envelope.subType); + assertEquals(udmi.schema.Envelope.SubFolder.BLOBSET, published.envelope.subFolder); + org.junit.jupiter.api.Assertions.assertNull(published.envelope.gatewayId); + } + class FakeDataRef extends DataRef { private final Map data; diff --git a/udmis/src/test/java/com/google/bos/udmi/service/core/ReflectProcessorTest.java b/udmis/src/test/java/com/google/bos/udmi/service/core/ReflectProcessorTest.java index 71e2e91923..be400efaeb 100644 --- a/udmis/src/test/java/com/google/bos/udmi/service/core/ReflectProcessorTest.java +++ b/udmis/src/test/java/com/google/bos/udmi/service/core/ReflectProcessorTest.java @@ -214,4 +214,29 @@ public void updateAwarenessPayloadLogging() { System.setOut(originalOut); } } + + @Test + public void downloadBlobsetProvisionTest() { + udmi.schema.EndpointConfiguration endpoint = new udmi.schema.EndpointConfiguration(); + endpoint.hostname = "localhost"; + endpoint.client_id = "r/test-reg/d/test-dev"; + udmi.schema.BlobsetDownload download = new udmi.schema.BlobsetDownload(); + download.blobs = new java.util.HashMap<>(ImmutableMap.of("_iot_endpoint_config", endpoint)); + + Envelope reflect = makeTestEnvelope(false); + reflect.transactionId = transactionId; + reflect.subType = SubType.DOWNLOAD; + reflect.subFolder = SubFolder.BLOBSET; + reflect.payload = encodeBase64(stringify(download)); + Bundle bundle = new Bundle(makeEnvelope(SubType.EVENTS, SubFolder.UDMI), reflect); + + activeTestInstance(() -> getReverseDispatcher().publish(bundle)); + + ArgumentCaptor endpointCaptor = + ArgumentCaptor.forClass(udmi.schema.EndpointConfiguration.class); + verify(provider, times(1)).provisionDownloadBlob( + eq(TEST_REGISTRY), eq(TEST_DEVICE), eq("_iot_endpoint_config"), endpointCaptor.capture()); + assertEquals("localhost", endpointCaptor.getValue().hostname); + assertEquals("r/test-reg/d/test-dev", endpointCaptor.getValue().client_id); + } } diff --git a/udmis/src/test/java/com/google/bos/udmi/service/core/StateProcessorTest.java b/udmis/src/test/java/com/google/bos/udmi/service/core/StateProcessorTest.java index a13568ed52..ae40d650ea 100644 --- a/udmis/src/test/java/com/google/bos/udmi/service/core/StateProcessorTest.java +++ b/udmis/src/test/java/com/google/bos/udmi/service/core/StateProcessorTest.java @@ -228,4 +228,20 @@ public void processMessagePayloadLogging() { System.setOut(originalOut); } } + + @Test + public void blobsetApplyTriggersDownload() { + initializeTestInstance(); + State stateMessage = getTestStateMessage(false, false); + stateMessage.blobset = new udmi.schema.BlobsetState(); + stateMessage.blobset.blobs = new java.util.HashMap<>(); + udmi.schema.BlobBlobsetState blobState = new udmi.schema.BlobBlobsetState(); + blobState.phase = udmi.schema.BlobBlobsetConfig.BlobPhase.APPLY; + stateMessage.blobset.blobs.put("_iot_endpoint_config", blobState); + + getReverseDispatcher().publish(new Bundle(getTestStateEnvelope(), stateMessage)); + terminateAndWait(); + + verify(provider, times(1)).handleBlobsetState(any(Envelope.class), any()); + } } diff --git a/udmis/src/test/java/com/google/bos/udmi/service/core/UploadProcessorTest.java b/udmis/src/test/java/com/google/bos/udmi/service/core/UploadProcessorTest.java new file mode 100644 index 0000000000..a73ef9fce8 --- /dev/null +++ b/udmis/src/test/java/com/google/bos/udmi/service/core/UploadProcessorTest.java @@ -0,0 +1,204 @@ +package com.google.bos.udmi.service.core; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.bos.udmi.service.pod.UdmiServicePod; +import com.google.bos.udmi.service.support.DataRef; +import com.google.bos.udmi.service.support.IotDataProvider; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import udmi.schema.DiscoveryUpload; +import udmi.schema.Envelope; + +class UploadProcessorTest { + + @TempDir + File tempDir; + + private Map sharedStore; + private IotDataProvider dataProvider; + private Envelope envelope; + + @BeforeEach + void setUp() { + UdmiServicePod.resetForTest(); + sharedStore = new ConcurrentHashMap<>(); + dataProvider = mock(IotDataProvider.class); + when(dataProvider.ref()).thenAnswer(inv -> new SharedMemoryDataRef(sharedStore)); + envelope = new Envelope(); + envelope.deviceRegistryId = "test-reg"; + envelope.deviceId = "SNP-1"; + } + + @AfterEach + void tearDown() { + UdmiServicePod.resetForTest(); + } + + @Test + void testOutOfOrderMultiPodReassemblyAndSha256Verification() throws Exception { + final byte[] chunk1Bytes = "PCAP-HEADER-AND-FIRST-PACKET-".getBytes(StandardCharsets.UTF_8); + final byte[] chunk2Bytes = "SECOND-PACKET-AND-FOOTER".getBytes(StandardCharsets.UTF_8); + final byte[] fullPayload = "PCAP-HEADER-AND-FIRST-PACKET-SECOND-PACKET-AND-FOOTER" + .getBytes(StandardCharsets.UTF_8); + final String expectedSha256 = UploadProcessor.computeSha256Hex(fullPayload); + final java.util.Date generation = + com.google.udmi.util.JsonUtil.getDate("2026-09-27T12:00:00Z"); + + // Pod 1 receives start marker (event_no = 0) + DiscoveryUpload startMsg = new DiscoveryUpload(); + startMsg.family = "bacnet"; + startMsg.generation = generation; + startMsg.event_no = 0; + assertNull(UploadProcessor.processDiscoveryUpload(envelope, startMsg, dataProvider, tempDir)); + + // Pod 2 receives chunk 2 out-of-order (event_no = 2) + DiscoveryUpload chunk2Msg = new DiscoveryUpload(); + chunk2Msg.family = "bacnet"; + chunk2Msg.generation = generation; + chunk2Msg.event_no = 2; + chunk2Msg.data = Base64.getEncoder().encodeToString(chunk2Bytes); + assertNull(UploadProcessor.processDiscoveryUpload(envelope, chunk2Msg, dataProvider, tempDir)); + + // Pod 3 receives terminal EOF marker (event_no = -3) before chunk 1 arrives + DiscoveryUpload eofMsg = new DiscoveryUpload(); + eofMsg.family = "bacnet"; + eofMsg.generation = generation; + eofMsg.event_no = -3; + eofMsg.sha256 = expectedSha256; + assertNull(UploadProcessor.processDiscoveryUpload(envelope, eofMsg, dataProvider, tempDir)); + + // Pod 1 receives chunk 1 (event_no = 1), completing the set and winning CAS election + DiscoveryUpload chunk1Msg = new DiscoveryUpload(); + chunk1Msg.family = "bacnet"; + chunk1Msg.generation = generation; + chunk1Msg.event_no = 1; + chunk1Msg.data = Base64.getEncoder().encodeToString(chunk1Bytes); + File reassembled = + UploadProcessor.processDiscoveryUpload(envelope, chunk1Msg, dataProvider, tempDir); + + assertNotNull(reassembled); + assertTrue(reassembled.exists()); + assertArrayEquals(fullPayload, Files.readAllBytes(reassembled.toPath())); + assertTrue(sharedStore.isEmpty(), "Staged DataRef entries must be cleaned up after reassembly"); + } + + @Test + void testSha256MismatchFailsFastAndCleansUp() { + final byte[] chunk1Bytes = "corrupted-chunk".getBytes(StandardCharsets.UTF_8); + final java.util.Date generation = com.google.udmi.util.JsonUtil.getDate("2026-09-27T12:05:00Z"); + + DiscoveryUpload startMsg = new DiscoveryUpload(); + startMsg.family = "bacnet"; + startMsg.generation = generation; + startMsg.event_no = 0; + assertNull(UploadProcessor.processDiscoveryUpload(envelope, startMsg, dataProvider, tempDir)); + + DiscoveryUpload chunk1Msg = new DiscoveryUpload(); + chunk1Msg.family = "bacnet"; + chunk1Msg.generation = generation; + chunk1Msg.event_no = 1; + chunk1Msg.data = Base64.getEncoder().encodeToString(chunk1Bytes); + assertNull(UploadProcessor.processDiscoveryUpload(envelope, chunk1Msg, dataProvider, tempDir)); + + DiscoveryUpload eofMsg = new DiscoveryUpload(); + eofMsg.family = "bacnet"; + eofMsg.generation = generation; + eofMsg.event_no = -2; + eofMsg.sha256 = "0000000000000000000000000000000000000000000000000000000000000000"; + + IllegalStateException thrown = assertThrows(IllegalStateException.class, + () -> UploadProcessor.processDiscoveryUpload(envelope, eofMsg, dataProvider, tempDir)); + assertTrue(thrown.getMessage().contains("SHA-256 mismatch")); + assertEquals(0, sharedStore.size(), "Staged DataRef entries must be cleaned up on failure"); + } + + private static class SharedMemoryDataRef extends DataRef { + private final Map data; + + SharedMemoryDataRef(Map data) { + this.data = data; + } + + private String getKeyPath(String key) { + return (registryId != null ? "r/" + registryId : "") + + (deviceId != null ? "/d/" + deviceId : "") + + (collection != null ? "/c/" + collection : "") + + ":" + + key; + } + + @Override + public void delete(String key) { + data.remove(getKeyPath(key)); + } + + @Override + public Map entries() { + String prefix = getKeyPath(""); + Map res = new HashMap<>(); + for (Map.Entry entry : data.entrySet()) { + if (entry.getKey().startsWith(prefix)) { + res.put(entry.getKey().substring(prefix.length()), entry.getValue()); + } + } + return res; + } + + @Override + public String get(String key) { + return data.get(getKeyPath(key)); + } + + @Override + public AutoCloseable lock() { + return () -> {}; + } + + @Override + public void put(String key, String value) { + data.put(getKeyPath(key), value); + } + + @Override + public void update(Map puts, Set deletes) { + if (puts != null) { + puts.forEach(this::put); + } + if (deletes != null) { + deletes.forEach(this::delete); + } + } + + @Override + public boolean updateIfMatch(String matchKey, String expectedValue, Map puts, + Set deletes) { + synchronized (data) { + String current = get(matchKey); + if ((expectedValue == null && current == null) + || (expectedValue != null && expectedValue.equals(current))) { + update(puts, deletes); + return true; + } + return false; + } + } + } +} diff --git a/udmis/src/test/java/com/google/bos/udmi/service/core/UufiProcessorTest.java b/udmis/src/test/java/com/google/bos/udmi/service/core/UufiProcessorTest.java index 69c3620b0a..317691edd0 100644 --- a/udmis/src/test/java/com/google/bos/udmi/service/core/UufiProcessorTest.java +++ b/udmis/src/test/java/com/google/bos/udmi/service/core/UufiProcessorTest.java @@ -147,6 +147,43 @@ public void inboundConfigRoutingTest() { assertNotNull(pointsetMap.get("points")); } + /** + * Test that inbound UUFI-wrapped download/blobset messages provision download blobs. + */ + @Test + public void inboundDownloadRoutingTest() { + Envelope innerEnvelope = new Envelope(); + innerEnvelope.subType = SubType.DOWNLOAD; + innerEnvelope.subFolder = SubFolder.BLOBSET; + innerEnvelope.deviceId = "dev-1"; + innerEnvelope.deviceRegistryId = "reg-1"; + + Map uufiWrapper = toMap(innerEnvelope); + Map innerPayload = Map.of( + "version", "1.5.2", + "timestamp", "2026-07-28T00:00:00Z", + "blobs", Map.of("_iot_endpoint_config", + Map.of("hostname", "localhost", "client_id", "r/reg-1/d/dev-1"))); + uufiWrapper.put("payload", innerPayload); + + Envelope transportEnvelope = new Envelope(); + transportEnvelope.source = "test-client"; + transportEnvelope.gatewayId = "uufi"; + + activeTestInstance(() -> getReverseDispatcher().publish( + new Bundle(transportEnvelope, uufiWrapper))); + + ArgumentCaptor endpointCaptor = + ArgumentCaptor.forClass(udmi.schema.EndpointConfiguration.class); + verify(provider, times(1)).provisionDownloadBlob( + org.mockito.ArgumentMatchers.eq("reg-1"), + org.mockito.ArgumentMatchers.eq("dev-1"), + org.mockito.ArgumentMatchers.eq("_iot_endpoint_config"), + endpointCaptor.capture()); + assertEquals("localhost", endpointCaptor.getValue().hostname); + assertEquals("r/reg-1/d/dev-1", endpointCaptor.getValue().client_id); + } + /** * Test that outbound system messages are correctly wrapped for UUFI clients. */ diff --git a/udmis/src/test/java/com/google/bos/udmi/service/messaging/impl/SimpleMqttPipeTest.java b/udmis/src/test/java/com/google/bos/udmi/service/messaging/impl/SimpleMqttPipeTest.java index 3f714fcd4f..fc2a4eae56 100644 --- a/udmis/src/test/java/com/google/bos/udmi/service/messaging/impl/SimpleMqttPipeTest.java +++ b/udmis/src/test/java/com/google/bos/udmi/service/messaging/impl/SimpleMqttPipeTest.java @@ -112,5 +112,39 @@ public void staticEnvelopeTopic() { assertEquals("events", legacyPointset.get(SUBTYPE_PROPERTY_KEY)); assertEquals("pointset", legacyPointset.get(SUBFOLDER_PROPERTY_KEY)); assertEquals(3, legacyPointset.keySet().size()); + + Map discoveryUpload = parseEnvelopeTopic("/r/reg/d/dev/upload/discovery"); + assertEquals("reg", discoveryUpload.get(REGISTRY_ID_PROPERTY_KEY)); + assertEquals("dev", discoveryUpload.get(DEVICE_ID_KEY)); + assertEquals("upload", discoveryUpload.get(SUBTYPE_PROPERTY_KEY)); + assertEquals("discovery", discoveryUpload.get(SUBFOLDER_PROPERTY_KEY)); + + Map blobsetDownload = parseEnvelopeTopic("/r/reg/d/dev/download/blobset"); + assertEquals("reg", blobsetDownload.get(REGISTRY_ID_PROPERTY_KEY)); + assertEquals("dev", blobsetDownload.get(DEVICE_ID_KEY)); + assertEquals("download", blobsetDownload.get(SUBTYPE_PROPERTY_KEY)); + assertEquals("blobset", blobsetDownload.get(SUBFOLDER_PROPERTY_KEY)); + } + + @Test + public void downloadMessageQosAndRetain() { + MessageBase.Bundle downloadBundle = new MessageBase.Bundle(); + downloadBundle.envelope = new udmi.schema.Envelope(); + downloadBundle.envelope.subType = udmi.schema.Envelope.SubType.DOWNLOAD; + downloadBundle.envelope.subFolder = udmi.schema.Envelope.SubFolder.BLOBSET; + downloadBundle.payload = "{}"; + org.eclipse.paho.client.mqttv3.MqttMessage downloadMsg = + SimpleMqttPipe.makeMqttMessage(downloadBundle); + assertEquals(0, downloadMsg.getQos()); + assertEquals(false, downloadMsg.isRetained()); + + MessageBase.Bundle configBundle = new MessageBase.Bundle(); + configBundle.envelope = new udmi.schema.Envelope(); + configBundle.envelope.subType = udmi.schema.Envelope.SubType.CONFIG; + configBundle.payload = "{}"; + org.eclipse.paho.client.mqttv3.MqttMessage configMsg = + SimpleMqttPipe.makeMqttMessage(configBundle); + assertEquals(1, configMsg.getQos()); + assertEquals(true, configMsg.isRetained()); } } \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_blobset.attr b/validator/sequences/endpoint_connection_download/config_blobset.attr new file mode 100644 index 0000000000..13880c0a10 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_blobset.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "blobset", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_blobset.json b/validator/sequences/endpoint_connection_download/config_blobset.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_blobset.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_discovery.attr b/validator/sequences/endpoint_connection_download/config_discovery.attr new file mode 100644 index 0000000000..80fa7de9d7 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_discovery.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "discovery", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_discovery.json b/validator/sequences/endpoint_connection_download/config_discovery.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_discovery.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_localnet.attr b/validator/sequences/endpoint_connection_download/config_localnet.attr new file mode 100644 index 0000000000..982694e31c --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_localnet.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "localnet", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_localnet.json b/validator/sequences/endpoint_connection_download/config_localnet.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_localnet.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_pointset.attr b/validator/sequences/endpoint_connection_download/config_pointset.attr new file mode 100644 index 0000000000..448196225d --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_pointset.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "pointset", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_pointset.json b/validator/sequences/endpoint_connection_download/config_pointset.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_pointset.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_system.attr b/validator/sequences/endpoint_connection_download/config_system.attr new file mode 100644 index 0000000000..cd271d7aa4 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_system.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "system", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_system.json b/validator/sequences/endpoint_connection_download/config_system.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_system.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_update.attr b/validator/sequences/endpoint_connection_download/config_update.attr new file mode 100644 index 0000000000..e3f64afb75 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_update.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "update", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/config_update.json b/validator/sequences/endpoint_connection_download/config_update.json new file mode 100644 index 0000000000..11e29da909 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/config_update.json @@ -0,0 +1,48 @@ +{ + "blobset" : { + "blobs" : { } + }, + "discovery" : { + "families" : { + "bacnet" : { }, + "vendor" : { } + } + }, + "localnet" : { + "families" : { + "bacnet" : { }, + "ether" : { }, + "ipv4" : { }, + "vendor" : { } + } + }, + "pointset" : { + "points" : { + "filter_alarm_pressure_status" : { + "ref" : "BV11.present_value", + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "ref" : "AV12.present_value", + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "set_value" : 98, + "units" : "Bars" + } + }, + "sample_rate_sec" : 300 + }, + "system" : { + "metrics_rate_sec" : 10, + "min_loglevel" : 100, + "operation" : { + "last_start" : "TIMESTAMP" + }, + "testing" : { + "sequence_name" : "endpoint_connection_download" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "@@@cloud udmi version###1.5.5-183-ga8572046b" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/device_system.log b/validator/sequences/endpoint_connection_download/device_system.log new file mode 100644 index 0000000000..ad1d0514ee --- /dev/null +++ b/validator/sequences/endpoint_connection_download/device_system.log @@ -0,0 +1,195 @@ +TIMESTAMP INFO device.log Configured with auth_type RS256 +TIMESTAMP INFO device.log Using addresses from default interface: ens4 for family: ether +TIMESTAMP INFO device.log Family ether address is 42:01:ac:11:23:ac +TIMESTAMP INFO device.log Using addresses from default interface: ens4 for family: ipv4 +TIMESTAMP INFO device.log Family ipv4 address is 172.17.35.172 +TIMESTAMP INFO device.log Loading endpoint into persistent data from configuration +TIMESTAMP WARNING device.log Updating persistent store: +{ + "applied_blobs" : { }, + "endpoint" : { + "auth_provider" : { + "basic" : { + "password" : "38269d11", + "username" : "/r/ZZ-TRI-FECTA/d/AHU-1" + } + }, + "client_id" : "/r/ZZ-TRI-FECTA/d/AHU-1", + "hostname" : "localhost", + "port" : 46432, + "protocol" : "mqtt", + "topic_prefix" : "/r/ZZ-TRI-FECTA/d/AHU-1", + "transport" : "ssl" + }, + "restart_count" : 1 +} +TIMESTAMP INFO device.log Starting pubber AHU-1, serial sequencer-15295, mac null, gateway null, options noPersist +TIMESTAMP INFO device.log Initializing mock JGit module in /usr/local/google/home/heykhyati/Projects/udmi/out/pubber_module_repo_sequencer-15295 +TIMESTAMP INFO device.log Isolated JGit repo initialized successfully. +TIMESTAMP INFO device.log Loading device key bytes from /usr/local/google/home/heykhyati/Projects/udmi/sites/udmi_site_model/devices/AHU-1/rsa_private.pkcs8 +TIMESTAMP INFO device.log CA cert file: /usr/local/google/home/heykhyati/Projects/udmi/sites/udmi_site_model/reflector/ca.crt +TIMESTAMP INFO device.log Device cert file: /usr/local/google/home/heykhyati/Projects/udmi/sites/udmi_site_model/devices/AHU-1/rsa_private.crt +TIMESTAMP INFO device.log Private key file: /usr/local/google/home/heykhyati/Projects/udmi/sites/udmi_site_model/devices/AHU-1/rsa_private.pem +TIMESTAMP INFO device.log Password sha256 6b5344e1 +TIMESTAMP WARNING device.log Creating new config latch for AHU-1 +TIMESTAMP +TIMESTAMP WARNING device.log Dropping state update until config received... +TIMESTAMP +TIMESTAMP WARNING device.log Dropping state update until config received... +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP +TIMESTAMP WARNING device.log Dropping state update until config received... +TIMESTAMP INFO device.log Connection complete. +TIMESTAMP WARNING device.log Start waiting 10s for config latch for AHU-1 +TIMESTAMP WARNING device.log Received config for config latch AHU-1 +TIMESTAMP WARNING device.log Starting AHU-1 PubberPublisherHost sender with delay 10s +TIMESTAMP +TIMESTAMP WARNING device.log Starting AHU-1 PubberSystemManager sender with delay 10s +TIMESTAMP WARNING device.log Starting AHU-1 PubberPointsetManager sender with delay 300s +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1995ms +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer 1915ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1908ms +TIMESTAMP DEBUG device.log State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer -1669ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log Config update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log State update defer 2000ms +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1994ms +TIMESTAMP DEBUG device.log State update defer 814ms +TIMESTAMP DEBUG device.log State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer -96ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1998ms +TIMESTAMP DEBUG device.log State update defer 1997ms +TIMESTAMP DEBUG device.log State update defer 1997ms +TIMESTAMP DEBUG device.log State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer -1052ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1993ms +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer 1980ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log State update defer 1978ms +TIMESTAMP DEBUG device.log Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1972ms +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer 963ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log State update defer 962ms +TIMESTAMP DEBUG device.log Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 953ms +TIMESTAMP DEBUG device.log State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG blobset.blob.receive success +TIMESTAMP DEBUG blobset.blob.parse success +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer 1928ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log State update defer 1927ms +TIMESTAMP DEBUG device.log Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1921ms +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer 945ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log State update defer 944ms +TIMESTAMP DEBUG device.log Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 941ms +TIMESTAMP DEBUG device.log State update defer 941ms +TIMESTAMP DEBUG device.log State update defer 940ms +TIMESTAMP DEBUG device.log State update defer 940ms +TIMESTAMP DEBUG device.log State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG blobset.blob.receive success +TIMESTAMP DEBUG blobset.blob.parse success +TIMESTAMP INFO device.log New config blob endpoint detected: +{ + "auth_provider" : { + "basic" : { + "password" : "38269d11", + "username" : "/r/ZZ-TRI-FECTA/d/AHU-1" + } + }, + "client_id" : "/r/ZZ-TRI-FECTA/d/AHU-1", + "generation" : "TIMESTAMP", + "hostname" : "localhost:46432", + "port" : 46432, + "protocol" : "mqtt", + "topic_prefix" : "/r/ZZ-TRI-FECTA/d/AHU-1", + "transport" : "ssl" +} +TIMESTAMP NOTICE validation.feature.sequence Sequence complete diff --git a/validator/sequences/endpoint_connection_download/download_blobset.attr b/validator/sequences/endpoint_connection_download/download_blobset.attr new file mode 100644 index 0000000000..c2a4d17e41 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/download_blobset.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "blobset", + "subType" : "download", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/download_blobset.json b/validator/sequences/endpoint_connection_download/download_blobset.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/download_blobset.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/events_pointset.attr b/validator/sequences/endpoint_connection_download/events_pointset.attr new file mode 100644 index 0000000000..018aaaec2a --- /dev/null +++ b/validator/sequences/endpoint_connection_download/events_pointset.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "pointset", + "subType" : "events", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/events_pointset.json b/validator/sequences/endpoint_connection_download/events_pointset.json new file mode 100644 index 0000000000..4b499d6ba4 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/events_pointset.json @@ -0,0 +1,15 @@ +{ + "points" : { + "filter_alarm_pressure_status" : { + "present_value" : false + }, + "filter_differential_pressure_sensor" : { + "present_value" : 8 + }, + "filter_differential_pressure_setpoint" : { + "present_value" : 98 + } + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/events_system.attr b/validator/sequences/endpoint_connection_download/events_system.attr new file mode 100644 index 0000000000..fe8fc134b0 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/events_system.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "system", + "subType" : "events", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/events_system.json b/validator/sequences/endpoint_connection_download/events_system.json new file mode 100644 index 0000000000..1ada2c1969 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/events_system.json @@ -0,0 +1,189 @@ +{ + "event_no" : 5, + "last_config" : "TIMESTAMP", + "logentries" : [ { + "category" : "blobset.blob.receive", + "level" : 100, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "blobset.blob.parse", + "level" : 100, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "system.config.receive", + "level" : 100, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 1928ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "system.config.parse", + "level" : 100, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 200, + "message" : "Device AHU-1 config handler", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 1927ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "detail" : "{\n \"blobset\" : {\n \"blobs\" : {\n \"_iot_endpoint_config\" : {\n \"generation\" : \"TIMESTAMP\",\n \"version\" : \"1.5.5-183-ga8572046b\"\n}", + "level" : 100, + "message" : "Config update AHU-1 (endpoint_connection_download)", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 200, + "message" : "TIMESTAMP", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "value of noWriteback: false", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "value of noWriteback: false", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "value of noWriteback: false", + "timestamp" : "TIMESTAMP" + }, { + "category" : "system.config.apply", + "level" : 300, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 1921ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "system.config.receive", + "level" : 100, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 945ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "system.config.parse", + "level" : 100, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 200, + "message" : "Device AHU-1 config handler", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 944ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "detail" : "{\n \"blobset\" : {\n \"blobs\" : {\n \"_iot_endpoint_config\" : {\n \"generation\" : \"TIMESTAMP\",\n \"version\" : \"1.5.5-183-ga8572046b\"\n}", + "level" : 100, + "message" : "Config update AHU-1 (endpoint_connection_download)", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 200, + "message" : "TIMESTAMP", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "value of noWriteback: false", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "value of noWriteback: false", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "value of noWriteback: false", + "timestamp" : "TIMESTAMP" + }, { + "category" : "system.config.apply", + "level" : 300, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 941ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 941ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 940ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 940ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer 0ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 200, + "message" : "Update state TIMESTAMP", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "detail" : "{\n \"blobset\" : {\n \"blobs\" : {\n \"_iot_endpoint_config\" : {\n \"generation\" : \"TIMESTAMP\",\n \"version\" : \"1.5.7\"\n}", + "level" : 100, + "message" : "State update AHU-1 (endpoint_connection_download)", + "timestamp" : "TIMESTAMP" + }, { + "category" : "blobset.blob.receive", + "level" : 100, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "blobset.blob.parse", + "level" : 100, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 200, + "message" : "New config blob endpoint detected:\n{\n \"auth_provider\" : {\n \"basic\" : {\n \"password\" : \"38269d11\",\n \"username\" : \"/r/ZZ-TRI-FECTA/d/AHU-1\"\n }\n },\n \"client_id\" : \"/r/ZZ-TRI-FECTA/d/AHU-1\",\n \"generation\" : \"TIMESTAMP-TRI-FECTA/d/AHU-1\",\n \"transport\" : \"ssl\"\n}", + "timestamp" : "TIMESTAMP" + } ], + "metrics" : { + "mem_free_mb" : 76.25149536132812, + "mem_total_mb" : 112.0 + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/local_blobset.json b/validator/sequences/endpoint_connection_download/local_blobset.json new file mode 100644 index 0000000000..44cc61c713 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/local_blobset.json @@ -0,0 +1,3 @@ +{ + "blobs" : { } +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/local_discovery.json b/validator/sequences/endpoint_connection_download/local_discovery.json new file mode 100644 index 0000000000..940f2e5e87 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/local_discovery.json @@ -0,0 +1,6 @@ +{ + "families" : { + "bacnet" : { }, + "vendor" : { } + } +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/local_localnet.json b/validator/sequences/endpoint_connection_download/local_localnet.json new file mode 100644 index 0000000000..020f9d4dfd --- /dev/null +++ b/validator/sequences/endpoint_connection_download/local_localnet.json @@ -0,0 +1,8 @@ +{ + "families" : { + "bacnet" : { }, + "ether" : { }, + "ipv4" : { }, + "vendor" : { } + } +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/local_pointset.json b/validator/sequences/endpoint_connection_download/local_pointset.json new file mode 100644 index 0000000000..0180ad7667 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/local_pointset.json @@ -0,0 +1,17 @@ +{ + "points" : { + "filter_alarm_pressure_status" : { + "ref" : "BV11.present_value", + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "ref" : "AV12.present_value", + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "set_value" : 98, + "units" : "Bars" + } + }, + "sample_rate_sec" : 300 +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/local_system.json b/validator/sequences/endpoint_connection_download/local_system.json new file mode 100644 index 0000000000..55877f2dd2 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/local_system.json @@ -0,0 +1,10 @@ +{ + "metrics_rate_sec" : 10, + "min_loglevel" : 100, + "operation" : { + "last_start" : "TIMESTAMP" + }, + "testing" : { + "sequence_name" : "endpoint_connection_download" + } +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/local_update.json b/validator/sequences/endpoint_connection_download/local_update.json new file mode 100644 index 0000000000..ecaa39053e --- /dev/null +++ b/validator/sequences/endpoint_connection_download/local_update.json @@ -0,0 +1,45 @@ +{ + "discovery" : { + "families" : { + "bacnet" : { }, + "vendor" : { } + } + }, + "localnet" : { + "families" : { + "bacnet" : { }, + "ether" : { }, + "ipv4" : { }, + "vendor" : { } + } + }, + "pointset" : { + "points" : { + "filter_alarm_pressure_status" : { + "ref" : "BV11.present_value", + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "ref" : "AV12.present_value", + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "set_value" : 98, + "units" : "Bars" + } + }, + "sample_rate_sec" : 300 + }, + "system" : { + "metrics_rate_sec" : 10, + "min_loglevel" : 100, + "operation" : { + "last_start" : "TIMESTAMP" + }, + "testing" : { + "sequence_name" : "endpoint_connection_download" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "@@@cloud udmi version###1.5.5-183-ga8572046b" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/sequence.log b/validator/sequences/endpoint_connection_download/sequence.log new file mode 100644 index 0000000000..a33166a8d4 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/sequence.log @@ -0,0 +1,1871 @@ +TIMESTAMP NOTICE Starting test endpoint_connection_download ################################ +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "gateway_id" because "com.google.daq.mqtt.sequencer.sequences.BlobsetSequences.deviceMetadata.gateway" is null, at com.google.daq.mqtt.sequencer.sequences.BlobsetSequences.lambda$setUp$0(BlobsetSequences.java:86) +TIMESTAMP INFO Stage start waiting for starting test wrapper at @0s +TIMESTAMP DEBUG Clear configTransactions and reset device config +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$resetDeviceConfig$14(SequenceBase.java:819) +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Set extraFieldChanged false because extra_field null +TIMESTAMP DEBUG Allowing device state change (everything) +TIMESTAMP DEBUG Starting reset_config full reset true +TIMESTAMP DEBUG Clear configTransactions and reset device config +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$resetDeviceConfig$14(SequenceBase.java:819) +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Set extraFieldChanged false because extra_field null +TIMESTAMP DEBUG Set extraFieldChanged true because extra_field reset_config +TIMESTAMP DEBUG Clear configTransactions and reset device config +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$resetDeviceConfig$14(SequenceBase.java:819) +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Set extraFieldChanged true because extra_field null +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$ensureStateConfigHoldoff$49(SequenceBase.java:1448) +TIMESTAMP is -6118ms +TIMESTAMP TRACE Updated check config_update: true +TIMESTAMP DEBUG Update config_update, adding configTransaction RC:0162e1.00000005 +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP +TIMESTAMP, because soft reset +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @8s +TIMESTAMP INFO Stage start waiting for config update synchronized at @8s +TIMESTAMP DEBUG Received command AHU-1/state/validation as RC:0162e1.00000003 +TIMESTAMP TRACE Received state_validation: { +TIMESTAMP TRACE Received state_validation: "operation" : "REPLY" +TIMESTAMP TRACE Received state_validation: } +TIMESTAMP DEBUG Handling device message state_validation RC:0162e1.00000003 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP DEBUG Received command AHU-1/events/pointset as MP:bcdb15f8 +TIMESTAMP TRACE Received events_pointset: { +TIMESTAMP TRACE Received events_pointset: "points" : { +TIMESTAMP TRACE Received events_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received events_pointset: "present_value" : false +TIMESTAMP TRACE Received events_pointset: }, +TIMESTAMP TRACE Received events_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received events_pointset: "present_value" : 8 +TIMESTAMP TRACE Received events_pointset: }, +TIMESTAMP TRACE Received events_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received events_pointset: "present_value" : 98 +TIMESTAMP TRACE Received events_pointset: } +TIMESTAMP TRACE Received events_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received events_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received events_pointset: } +TIMESTAMP DEBUG Handling device message events_pointset MP:bcdb15f8 +TIMESTAMP DEBUG Capturing AHU-1 message events_pointset +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:dbba8ddf +TIMESTAMP INFO device.log: Configured with auth_type RS256 +TIMESTAMP INFO device.log: Using addresses from default interface: ens4 for family: ether +TIMESTAMP INFO device.log: Family ether address is 42:01:ac:11:23:ac +TIMESTAMP INFO device.log: Using addresses from default interface: ens4 for family: ipv4 +TIMESTAMP INFO device.log: Family ipv4 address is 172.17.35.172 +TIMESTAMP INFO device.log: Loading endpoint into persistent data from configuration +TIMESTAMP WARNING device.log: Updating persistent store: +{ + "applied_blobs" : { }, + "endpoint" : { + "auth_provider" : { + "basic" : { + "password" : "38269d11", + "username" : "/r/ZZ-TRI-FECTA/d/AHU-1" + } + }, + "client_id" : "/r/ZZ-TRI-FECTA/d/AHU-1", + "hostname" : "localhost", + "port" : 46432, + "protocol" : "mqtt", + "topic_prefix" : "/r/ZZ-TRI-FECTA/d/AHU-1", + "transport" : "ssl" + }, + "restart_count" : 1 +} +TIMESTAMP INFO device.log: Starting pubber AHU-1, serial sequencer-15295, mac null, gateway null, options noPersist +TIMESTAMP INFO device.log: Initializing mock JGit module in /usr/local/google/home/heykhyati/Projects/udmi/out/pubber_module_repo_sequencer-15295 +TIMESTAMP INFO device.log: Isolated JGit repo initialized successfully. +TIMESTAMP INFO device.log: Loading device key bytes from /usr/local/google/home/heykhyati/Projects/udmi/sites/udmi_site_model/devices/AHU-1/rsa_private.pkcs8 +TIMESTAMP INFO device.log: CA cert file: /usr/local/google/home/heykhyati/Projects/udmi/sites/udmi_site_model/reflector/ca.crt +TIMESTAMP INFO device.log: Device cert file: /usr/local/google/home/heykhyati/Projects/udmi/sites/udmi_site_model/devices/AHU-1/rsa_private.crt +TIMESTAMP INFO device.log: Private key file: /usr/local/google/home/heykhyati/Projects/udmi/sites/udmi_site_model/devices/AHU-1/rsa_private.pem +TIMESTAMP INFO device.log: Password sha256 6b5344e1 +TIMESTAMP WARNING device.log: Creating new config latch for AHU-1 +TIMESTAMP +TIMESTAMP WARNING device.log: Dropping state update until config received... +TIMESTAMP +TIMESTAMP WARNING device.log: Dropping state update until config received... +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP +TIMESTAMP WARNING device.log: Dropping state update until config received... +TIMESTAMP INFO device.log: Connection complete. +TIMESTAMP WARNING device.log: Start waiting 10s for config latch for AHU-1 +TIMESTAMP WARNING device.log: Received config for config latch AHU-1 +TIMESTAMP WARNING device.log: Starting AHU-1 PubberPublisherHost sender with delay 10s +TIMESTAMP +TIMESTAMP WARNING device.log: Starting AHU-1 PubberSystemManager sender with delay 10s +TIMESTAMP WARNING device.log: Starting AHU-1 PubberPointsetManager sender with delay 300s +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG Handling device message events_system MP:dbba8ddf +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP DEBUG Received command AHU-1/state/validation as RC:0162e1.00000004 +TIMESTAMP TRACE Received state_validation: { +TIMESTAMP TRACE Received state_validation: "operation" : "REPLY" +TIMESTAMP TRACE Received state_validation: } +TIMESTAMP DEBUG Handling device message state_validation RC:0162e1.00000004 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:496052d0 +TIMESTAMP DEBUG Received events_system (no logs) +TIMESTAMP DEBUG Handling device message events_system MP:496052d0 +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.00000005 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.00000005 +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.00000005 +TIMESTAMP +TIMESTAMP RC:0162e1.00000005 +TIMESTAMP INFO Updated config #001: { +TIMESTAMP INFO Updated config #001: "discovery" : { +TIMESTAMP INFO Updated config #001: "families" : { +TIMESTAMP INFO Updated config #001: "bacnet" : { }, +TIMESTAMP INFO Updated config #001: "vendor" : { } +TIMESTAMP INFO Updated config #001: } +TIMESTAMP INFO Updated config #001: }, +TIMESTAMP INFO Updated config #001: "localnet" : { +TIMESTAMP INFO Updated config #001: "families" : { +TIMESTAMP INFO Updated config #001: "bacnet" : { }, +TIMESTAMP INFO Updated config #001: "ether" : { }, +TIMESTAMP INFO Updated config #001: "ipv4" : { }, +TIMESTAMP INFO Updated config #001: "vendor" : { } +TIMESTAMP INFO Updated config #001: } +TIMESTAMP INFO Updated config #001: }, +TIMESTAMP INFO Updated config #001: "pointset" : { +TIMESTAMP INFO Updated config #001: "points" : { +TIMESTAMP INFO Updated config #001: "filter_alarm_pressure_status" : { +TIMESTAMP INFO Updated config #001: "ref" : "BV11.present_value", +TIMESTAMP INFO Updated config #001: "units" : "No-units" +TIMESTAMP INFO Updated config #001: }, +TIMESTAMP INFO Updated config #001: "filter_differential_pressure_sensor" : { +TIMESTAMP INFO Updated config #001: "ref" : "AV12.present_value", +TIMESTAMP INFO Updated config #001: "units" : "Degrees-Celsius" +TIMESTAMP INFO Updated config #001: }, +TIMESTAMP INFO Updated config #001: "filter_differential_pressure_setpoint" : { +TIMESTAMP INFO Updated config #001: "set_value" : 98, +TIMESTAMP INFO Updated config #001: "units" : "Bars" +TIMESTAMP INFO Updated config #001: } +TIMESTAMP INFO Updated config #001: }, +TIMESTAMP INFO Updated config #001: "sample_rate_sec" : 300 +TIMESTAMP INFO Updated config #001: }, +TIMESTAMP INFO Updated config #001: "system" : { +TIMESTAMP INFO Updated config #001: "metrics_rate_sec" : 10, +TIMESTAMP INFO Updated config #001: "min_loglevel" : 100, +TIMESTAMP INFO Updated config #001: "operation" : { +TIMESTAMP" +TIMESTAMP INFO Updated config #001: }, +TIMESTAMP INFO Updated config #001: "testing" : { +TIMESTAMP INFO Updated config #001: "sequence_name" : "endpoint_connection_download" +TIMESTAMP INFO Updated config #001: } +TIMESTAMP INFO Updated config #001: }, +TIMESTAMP", +TIMESTAMP INFO Updated config #001: "version" : "@@@cloud udmi version###1.5.5-183-ga8572046b" +TIMESTAMP INFO Updated config #001: } +TIMESTAMP +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @8s after 0s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @8s +TIMESTAMP DEBUG Config sync took 0s +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP DEBUG Saw last_config synchronized check disabled: missing state.system.last_config +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP +TIMESTAMP +TIMESTAMP TRACE Update config soft reset: Add `discovery` = { "families": { "vendor": { }, "bacnet": { } } } +TIMESTAMP TRACE Update config soft reset: Add `pointset` = { "sample_rate_sec": `300`, "points": { "filter_differential_pressure_setpoint": { "set_value": `98`, "units": `Bars` }, "filter_alarm_pressure_status": { "ref": `BV11.present_value`, "units": `No-units` }, "filter_differential_pressure_sensor": { "ref": `AV12.present_value`, "units": `Degrees-Celsius` } } } +TIMESTAMP TRACE Update config soft reset: Add `localnet` = { "families": { "ether": { }, "ipv4": { }, "vendor": { }, "bacnet": { } } } +TIMESTAMP TRACE Update config soft reset: Add `timestamp` = `generated timestamp` +TIMESTAMP DEBUG Done with reset_config +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @8s +TIMESTAMP INFO Stage start waiting for config update synchronized at @8s +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:ae6c3d58 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:ae6c3d58 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:ae6c3d58 +TIMESTAMP INFO Initial state #001: { +TIMESTAMP INFO Initial state #001: "discovery" : { +TIMESTAMP INFO Initial state #001: "families" : { } +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "localnet" : { +TIMESTAMP INFO Initial state #001: "families" : { +TIMESTAMP INFO Initial state #001: "bacnet" : { +TIMESTAMP INFO Initial state #001: "addr" : "27312" +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "ether" : { +TIMESTAMP INFO Initial state #001: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "ipv4" : { +TIMESTAMP INFO Initial state #001: "addr" : "172.17.35.172" +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "vendor" : { +TIMESTAMP INFO Initial state #001: "addr" : "28179023" +TIMESTAMP INFO Initial state #001: } +TIMESTAMP INFO Initial state #001: } +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "pointset" : { +TIMESTAMP INFO Initial state #001: "points" : { +TIMESTAMP INFO Initial state #001: "filter_alarm_pressure_status" : { +TIMESTAMP INFO Initial state #001: "units" : "No-units" +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "filter_differential_pressure_sensor" : { +TIMESTAMP INFO Initial state #001: "units" : "Degrees-Celsius" +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "filter_differential_pressure_setpoint" : { +TIMESTAMP INFO Initial state #001: "units" : "Bars", +TIMESTAMP INFO Initial state #001: "value_state" : "applied" +TIMESTAMP INFO Initial state #001: } +TIMESTAMP INFO Initial state #001: } +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "system" : { +TIMESTAMP INFO Initial state #001: "hardware" : { +TIMESTAMP INFO Initial state #001: "make" : "BOS", +TIMESTAMP INFO Initial state #001: "model" : "pubber" +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP", +TIMESTAMP INFO Initial state #001: "operation" : { +TIMESTAMP", +TIMESTAMP INFO Initial state #001: "mode" : "initial", +TIMESTAMP INFO Initial state #001: "operational" : true, +TIMESTAMP INFO Initial state #001: "restart_count" : 1 +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "serial_no" : "sequencer-15295", +TIMESTAMP INFO Initial state #001: "software" : { +TIMESTAMP INFO Initial state #001: "firmware" : "v1", +TIMESTAMP INFO Initial state #001: "system" : "v1" +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP INFO Initial state #001: "status" : { +TIMESTAMP INFO Initial state #001: "category" : "system.config.parse", +TIMESTAMP INFO Initial state #001: "level" : 100, +TIMESTAMP INFO Initial state #001: "message" : "success", +TIMESTAMP" +TIMESTAMP INFO Initial state #001: } +TIMESTAMP INFO Initial state #001: }, +TIMESTAMP", +TIMESTAMP INFO Initial state #001: "version" : "1.5.7" +TIMESTAMP INFO Initial state #001: } +TIMESTAMP NOTICE Received serial number sequencer-15295 +TIMESTAMP) +TIMESTAMP DEBUG Received command AHU-1/config/update as CU:0162e1.00000005 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update CU:0162e1.00000005 +TIMESTAMP DEBUG Ignoring operation reply CU:0162e1.00000005 +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:ae6c3d58-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.parse", +TIMESTAMP TRACE Received state_system: "level" : 100, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:ae6c3d58-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:ae6c3d58-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:ae6c3d58-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:ae6c3d58-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:ae6c3d58-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:ae6c3d58-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:ae6c3d58-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as MP:ae6c3d58 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update MP:ae6c3d58 +TIMESTAMP +TIMESTAMP MP:ae6c3d58 +TIMESTAMP INFO Updated config #002: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:9959d008 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:9959d008 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:9959d008 +TIMESTAMP INFO Updated state #002: Changed 4 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #002: Set `system.status.level` = `300` +TIMESTAMP INFO Updated state #002: Set `system.status.category` = `system.config.apply` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @10s after 2s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @10s +TIMESTAMP DEBUG Config sync took 2s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP DEBUG Disallowing device state change (everything) +TIMESTAMP WARNING Now using alternate connection client! +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @10s +TIMESTAMP INFO Stage start waiting for using alternate client at @10s +TIMESTAMP is -480ms +TIMESTAMP DEBUG Forcing config update +TIMESTAMP TRACE Updated check config_update: true +TIMESTAMP DEBUG Update config_update, adding configTransaction RC:0162e1.00000006 +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:9959d008-0 +TIMESTAMP","version":"1.5.7"} +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:9959d008-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:9959d008-1 +TIMESTAMP","version":"1.5.7"} +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:9959d008-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:9959d008-2 +TIMESTAMP","version":"1.5.7"} +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:9959d008-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:9959d008-3 +TIMESTAMP","version":"1.5.7"} +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:9959d008-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.00000006 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.00000006 +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.00000006 +TIMESTAMP +TIMESTAMP RC:0162e1.00000006 +TIMESTAMP INFO Updated config #003: Changed 2 fields: +TIMESTAMP INFO Updated config #003: Add `system.testing.endpoint_type` = `alternate` +TIMESTAMP INFO Updated config #003: Add `blobset` = { "blobs": { "_iot_endpoint_config": { "phase": `final`, "generation": `blob generation`, "sha256": `blob data hash`, "url": `endpoint data` } } } +TIMESTAMP +TIMESTAMP DEBUG Detail pending config transaction is now: null +TIMESTAMP +TIMESTAMP +TIMESTAMP TRACE Update config reset alternate registry: Add `blobset` = { "blobs": { "_iot_endpoint_config": { "phase": `final`, "generation": `blob generation`, "sha256": `blob data hash`, "url": `endpoint data` } } } +TIMESTAMP DEBUG Stage finished waiting for using alternate client at @11s after 1s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @11s +TIMESTAMP WARNING Done with alternate connection client! +TIMESTAMP is 1998ms +TIMESTAMP DEBUG Rate-limiting config by 1998ms +TIMESTAMP TRACE Updated check config_update: true +TIMESTAMP DEBUG Update config_update, adding configTransaction RC:0162e1.00000007 +TIMESTAMP +TIMESTAMP, because initial setup +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @13s +TIMESTAMP INFO Stage start waiting for config update synchronized at @13s +TIMESTAMP DEBUG Received command AHU-1/config/update as CU:0162e1.00000006 +TIMESTAMP DEBUG Received backup message AHU-1/config/update: {"operation":"REPLY"} +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update CU:0162e1.00000006 +TIMESTAMP DEBUG Ignoring operation reply CU:0162e1.00000006 +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.00000007 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.00000007 +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.00000007 +TIMESTAMP +TIMESTAMP RC:0162e1.00000007 +TIMESTAMP INFO Updated config #004: Changed 2 fields: +TIMESTAMP INFO Updated config #004: Remove `system.testing.endpoint_type` +TIMESTAMP INFO Updated config #004: Remove `blobset` +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:9cb807f8 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:9cb807f8 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:9cb807f8 +TIMESTAMP INFO Updated state #003: Changed 4 fields: +TIMESTAMP INFO Updated state #003: Set `system.status.level` = `100` +TIMESTAMP INFO Updated state #003: Set `system.status.category` = `system.config.parse` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/update as CU:0162e1.00000007 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update CU:0162e1.00000007 +TIMESTAMP DEBUG Ignoring operation reply CU:0162e1.00000007 +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:9cb807f8-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.parse", +TIMESTAMP TRACE Received state_system: "level" : 100, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:9cb807f8-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:9cb807f8-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:9cb807f8-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:9cb807f8-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:9cb807f8-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:9cb807f8-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:9cb807f8-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:5baedfd3 +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1995ms +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer 1915ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1908ms +TIMESTAMP DEBUG device.log: State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer -1669ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log: Config update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log: State update defer 2000ms +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1994ms +TIMESTAMP DEBUG device.log: State update defer 814ms +TIMESTAMP DEBUG Handling device message events_system MP:5baedfd3 +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:53ce2a9d +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:53ce2a9d +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:53ce2a9d +TIMESTAMP INFO Updated state #004: Changed 4 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #004: Set `system.status.level` = `300` +TIMESTAMP INFO Updated state #004: Set `system.status.category` = `system.config.apply` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @15s after 2s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @15s +TIMESTAMP DEBUG Config sync took 2s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP +TIMESTAMP +TIMESTAMP TRACE Update config initial setup: Remove `blobset` +TIMESTAMP +TIMESTAMP DEBUG Waiting for device stateTransaction RC:0162e1.00000008 (was null) +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @15s +TIMESTAMP INFO Stage start waiting for state query at @15s +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:53ce2a9d-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:53ce2a9d-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:53ce2a9d-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:53ce2a9d-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:53ce2a9d-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:53ce2a9d-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:53ce2a9d-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:53ce2a9d-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/update as RC:0162e1.00000008 +TIMESTAMP DEBUG Received state_update +TIMESTAMP INFO Received device configAcked +TIMESTAMP DEBUG Handling update message state_update RC:0162e1.00000008 +TIMESTAMP DEBUG Removed stateTransaction RC:0162e1.00000008 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP RC:0162e1.00000008 +TIMESTAMP INFO Updated state #005: Changed 0 fields. +TIMESTAMP) +TIMESTAMP DEBUG Stage finished waiting for state query at @16s after 1s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @16s +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @16s +TIMESTAMP INFO Stage start waiting for initial device state at @16s +TIMESTAMP is 952ms +TIMESTAMP DEBUG Rate-limiting config by 952ms +TIMESTAMP TRACE Updated check config_update: true +TIMESTAMP DEBUG Update config_update, adding configTransaction RC:0162e1.00000009 +TIMESTAMP +TIMESTAMP, because before initial device state +TIMESTAMP TRACE Stage suspend waiting for initial device state at @17s +TIMESTAMP INFO Stage start waiting for config update synchronized at @17s +TIMESTAMP DEBUG Received command AHU-1/reply/update as RC:0162e1.00000008 +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.00000009 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.00000009 +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.00000009 +TIMESTAMP +TIMESTAMP RC:0162e1.00000009 +TIMESTAMP INFO Updated config #005: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:f95b34e6 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:f95b34e6 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:f95b34e6 +TIMESTAMP INFO Updated state #006: Changed 4 fields: +TIMESTAMP INFO Updated state #006: Set `system.status.level` = `100` +TIMESTAMP INFO Updated state #006: Set `system.status.category` = `system.config.parse` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/update as CU:0162e1.00000009 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update CU:0162e1.00000009 +TIMESTAMP DEBUG Ignoring operation reply CU:0162e1.00000009 +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:f95b34e6-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.parse", +TIMESTAMP TRACE Received state_system: "level" : 100, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:f95b34e6-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:f95b34e6-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:f95b34e6-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:f95b34e6-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:f95b34e6-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:f95b34e6-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:f95b34e6-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:d7c31505 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:d7c31505 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:d7c31505 +TIMESTAMP INFO Updated state #007: Changed 4 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #007: Set `system.status.level` = `300` +TIMESTAMP INFO Updated state #007: Set `system.status.category` = `system.config.apply` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @20s after 3s +TIMESTAMP TRACE Stage resume waiting for initial device state at @20s +TIMESTAMP DEBUG Config sync took 3s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Stage finished waiting for initial device state at @20s after 4s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @20s +TIMESTAMP DEBUG Device state system status level is now 300 +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @20s +TIMESTAMP INFO Stage start waiting for config update synchronized at @20s +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:d7c31505-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:d7c31505-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:d7c31505-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:d7c31505-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @20s after 0s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @20s +TIMESTAMP DEBUG Config sync took 3s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @20s +TIMESTAMP INFO Stage start waiting for executing test at @20s +TIMESTAMP DEBUG stage begin waiting for executing test at @20s +TIMESTAMP DEBUG Allowing device state change blobset +TIMESTAMP DEBUG blobset download config: { +TIMESTAMP DEBUG blobset download config: "blobs" : { +TIMESTAMP DEBUG blobset download config: "_iot_endpoint_config" : { +TIMESTAMP", +TIMESTAMP DEBUG blobset download config: "phase" : "final", +TIMESTAMP DEBUG blobset download config: "sha256" : "@@@blob data hash###262da75e678b3696a9ea18bcce46ce27b364e4ac0f740b0e61a697aea4986dca", +TIMESTAMP DEBUG blobset download config: "url" : "@@@endpoint download url###download://_iot_endpoint_config" +TIMESTAMP DEBUG blobset download config: } +TIMESTAMP DEBUG blobset download config: } +TIMESTAMP DEBUG blobset download config: } +TIMESTAMP TRACE Stage suspend waiting for executing test at @20s +TIMESTAMP INFO Stage start waiting for blobset phase is final and stateStatus is null at @20s +TIMESTAMP is 1979ms +TIMESTAMP DEBUG Rate-limiting config by 1979ms +TIMESTAMP DEBUG System config extra field null +TIMESTAMP TRACE Updated check config_system: true +TIMESTAMP DEBUG Update config_system, adding configTransaction RC:0162e1.0000000b +TIMESTAMP TRACE Updated check config_pointset: true +TIMESTAMP DEBUG Update config_pointset, adding configTransaction RC:0162e1.0000000c +TIMESTAMP TRACE Updated check config_gateway: false +TIMESTAMP TRACE Updated check config_localnet: true +TIMESTAMP DEBUG Update config_localnet, adding configTransaction RC:0162e1.0000000d +TIMESTAMP TRACE Updated check config_blobset: true +TIMESTAMP DEBUG Update config_blobset, adding configTransaction RC:0162e1.0000000e +TIMESTAMP TRACE Updated check config_discovery: true +TIMESTAMP DEBUG Update config_discovery, adding configTransaction RC:0162e1.0000000f +TIMESTAMP +TIMESTAMP, because before blobset phase is final and stateStatus is null +TIMESTAMP TRACE Stage suspend waiting for blobset phase is final and stateStatus is null at @22s +TIMESTAMP INFO Stage start waiting for config update synchronized at @22s +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:d7c31505-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:d7c31505-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:d7c31505-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:d7c31505-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/download/blobset as RC:0162e1.0000000a +TIMESTAMP DEBUG Received download_blobset +TIMESTAMP DEBUG Handling device message download_blobset RC:0162e1.0000000a +TIMESTAMP TRACE Ignoring echo downloadTransaction RC:0162e1.0000000a +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.0000000b +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.0000000b +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.0000000b +TIMESTAMP +TIMESTAMP RC:0162e1.0000000b +TIMESTAMP INFO Updated config #006: Changed 1 fields: +TIMESTAMP INFO Updated config #006: Add `blobset` = { "blobs": { "_iot_endpoint_config": { "phase": `final`, "generation": `blob generation`, "sha256": `blob data hash`, "url": `endpoint download url` } } } +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/config/system as CU:0162e1.0000000b +TIMESTAMP TRACE Received config_system: { +TIMESTAMP TRACE Received config_system: "operation" : "REPLY" +TIMESTAMP TRACE Received config_system: } +TIMESTAMP DEBUG Handling device message config_system CU:0162e1.0000000b +TIMESTAMP TRACE Ignoring echo configTransaction CU:0162e1.0000000b +TIMESTAMP DEBUG Detail config update synchronized is now: device state not updated since config issued, config transactions not cleared, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:c93157f7 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:c93157f7 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:c93157f7 +TIMESTAMP INFO Updated state #008: Changed 4 fields: +TIMESTAMP INFO Updated state #008: Set `system.status.level` = `100` +TIMESTAMP INFO Updated state #008: Set `system.status.category` = `system.config.parse` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.0000000d +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.0000000d +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.0000000d +TIMESTAMP +TIMESTAMP RC:0162e1.0000000d +TIMESTAMP INFO Updated config #007: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: config transactions not cleared, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/localnet as CU:0162e1.0000000d +TIMESTAMP TRACE Received config_localnet: { +TIMESTAMP TRACE Received config_localnet: "operation" : "REPLY" +TIMESTAMP TRACE Received config_localnet: } +TIMESTAMP DEBUG Handling device message config_localnet CU:0162e1.0000000d +TIMESTAMP TRACE Ignoring echo configTransaction CU:0162e1.0000000d +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:c93157f7-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.parse", +TIMESTAMP TRACE Received state_system: "level" : 100, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:c93157f7-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:c93157f7-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:c93157f7-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:c93157f7-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:c93157f7-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:c93157f7-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:c93157f7-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.0000000e +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.0000000e +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.0000000e +TIMESTAMP +TIMESTAMP RC:0162e1.0000000e +TIMESTAMP INFO Updated config #008: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/config/blobset as CU:0162e1.0000000e +TIMESTAMP TRACE Received config_blobset: { +TIMESTAMP TRACE Received config_blobset: "operation" : "REPLY" +TIMESTAMP TRACE Received config_blobset: } +TIMESTAMP DEBUG Handling device message config_blobset CU:0162e1.0000000e +TIMESTAMP TRACE Ignoring echo configTransaction CU:0162e1.0000000e +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:f60345c9 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:f60345c9 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:f60345c9 +TIMESTAMP INFO Updated state #009: Changed 6 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #009: Set `system.status.level` = `300` +TIMESTAMP INFO Updated state #009: Set `system.status.category` = `system.config.apply` +TIMESTAMP` +TIMESTAMP` } } } +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: config transactions not cleared +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:f60345c9-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:f60345c9-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.0000000f +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.0000000f +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.0000000f +TIMESTAMP +TIMESTAMP RC:0162e1.0000000f +TIMESTAMP INFO Updated config #009: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: config transactions not cleared, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:f60345c9-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:f60345c9-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/discovery as CU:0162e1.0000000f +TIMESTAMP TRACE Received config_discovery: { +TIMESTAMP TRACE Received config_discovery: "operation" : "REPLY" +TIMESTAMP TRACE Received config_discovery: } +TIMESTAMP DEBUG Handling device message config_discovery CU:0162e1.0000000f +TIMESTAMP TRACE Ignoring echo configTransaction CU:0162e1.0000000f +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:5690e7c7 +TIMESTAMP DEBUG device.log: State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer -96ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log: Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1998ms +TIMESTAMP DEBUG device.log: State update defer 1997ms +TIMESTAMP DEBUG device.log: State update defer 1997ms +TIMESTAMP DEBUG device.log: State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer -1052ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log: Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1993ms +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer 1980ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: State update defer 1978ms +TIMESTAMP DEBUG device.log: Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1972ms +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer 963ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: State update defer 962ms +TIMESTAMP DEBUG device.log: Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 953ms +TIMESTAMP DEBUG device.log: State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG Handling device message events_system MP:5690e7c7 +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:f60345c9-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:f60345c9-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/blobset as MP:f60345c9-3 +TIMESTAMP TRACE Received state_blobset: { +TIMESTAMP TRACE Received state_blobset: "blobs" : { +TIMESTAMP TRACE Received state_blobset: "_iot_endpoint_config" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "phase" : "apply" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP TRACE Received state_blobset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP DEBUG Handling device message state_blobset MP:f60345c9-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:f60345c9-4 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:f60345c9-4 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.0000000c +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.0000000c +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.0000000c +TIMESTAMP +TIMESTAMP RC:0162e1.0000000c +TIMESTAMP INFO Updated config #010: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/config/pointset as CU:0162e1.0000000c +TIMESTAMP TRACE Received config_pointset: { +TIMESTAMP TRACE Received config_pointset: "operation" : "REPLY" +TIMESTAMP TRACE Received config_pointset: } +TIMESTAMP DEBUG Handling device message config_pointset CU:0162e1.0000000c +TIMESTAMP TRACE Ignoring echo configTransaction CU:0162e1.0000000c +TIMESTAMP DEBUG Detail config update synchronized is now: last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:c3db2a99 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:c3db2a99 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:c3db2a99 +TIMESTAMP INFO Updated state #010: Changed 3 fields: +TIMESTAMP` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @27s after 4s +TIMESTAMP TRACE Stage resume waiting for blobset phase is final and stateStatus is null at @27s +TIMESTAMP DEBUG Config sync took 4s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Recorded sequence: 1. Update config before blobset phase is final and stateStatus is null +TIMESTAMP INFO Device config Add `blobset` = { "blobs": { "_iot_endpoint_config": { "phase": `final`, "generation": `blob generation`, "sha256": `blob data hash`, "url": `endpoint download url` } } } +TIMESTAMP TRACE Update config before blobset phase is final and stateStatus is null: Add `blobset` = { "blobs": { "_iot_endpoint_config": { "phase": `final`, "generation": `blob generation`, "sha256": `blob data hash`, "url": `endpoint download url` } } } +TIMESTAMP DEBUG Recorded sequence: 1. Wait for blobset phase is final and stateStatus is null +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:c3db2a99-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:c3db2a99-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:c3db2a99-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:c3db2a99-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:c3db2a99-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:c3db2a99-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/blobset as MP:c3db2a99-3 +TIMESTAMP TRACE Received state_blobset: { +TIMESTAMP TRACE Received state_blobset: "blobs" : { +TIMESTAMP TRACE Received state_blobset: "_iot_endpoint_config" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "phase" : "apply" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP TRACE Received state_blobset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP DEBUG Handling device message state_blobset MP:c3db2a99-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:c3db2a99-4 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:c3db2a99-4 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command null/config/udmi as null +TIMESTAMP DEBUG Received command null/config/udmi as null +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:cbe82b9a +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:cbe82b9a +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:cbe82b9a +TIMESTAMP INFO Updated state #011: Changed 1 fields: +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:94c63c62 +TIMESTAMP DEBUG blobset.blob.receive: success +TIMESTAMP DEBUG blobset.blob.parse: success +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer 1928ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: State update defer 1927ms +TIMESTAMP DEBUG device.log: Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1921ms +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer 945ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: State update defer 944ms +TIMESTAMP DEBUG device.log: Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 941ms +TIMESTAMP DEBUG device.log: State update defer 941ms +TIMESTAMP DEBUG device.log: State update defer 940ms +TIMESTAMP DEBUG device.log: State update defer 940ms +TIMESTAMP DEBUG device.log: State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG blobset.blob.receive: success +TIMESTAMP DEBUG blobset.blob.parse: success +TIMESTAMP INFO device.log: New config blob endpoint detected: +{ + "auth_provider" : { + "basic" : { + "password" : "38269d11", + "username" : "/r/ZZ-TRI-FECTA/d/AHU-1" + } + }, + "client_id" : "/r/ZZ-TRI-FECTA/d/AHU-1", + "generation" : "TIMESTAMP", + "hostname" : "localhost:46432", + "port" : 46432, + "protocol" : "mqtt", + "topic_prefix" : "/r/ZZ-TRI-FECTA/d/AHU-1", + "transport" : "ssl" +} +TIMESTAMP DEBUG Handling device message events_system MP:94c63c62 +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:cbe82b9a-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:cbe82b9a-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:cbe82b9a-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:cbe82b9a-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:cbe82b9a-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:cbe82b9a-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/blobset as MP:cbe82b9a-3 +TIMESTAMP TRACE Received state_blobset: { +TIMESTAMP TRACE Received state_blobset: "blobs" : { +TIMESTAMP TRACE Received state_blobset: "_iot_endpoint_config" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "phase" : "apply" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP TRACE Received state_blobset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP DEBUG Handling device message state_blobset MP:cbe82b9a-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:cbe82b9a-4 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:cbe82b9a-4 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:f715b7f0 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:f715b7f0 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:f715b7f0 +TIMESTAMP INFO Updated state #012: Changed 3 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #012: Set `blobset.blobs._iot_endpoint_config.phase` = `final` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Stage finished waiting for blobset phase is final and stateStatus is null at @46s after 25s +TIMESTAMP TRACE Stage resume waiting for executing test at @46s +TIMESTAMP TRACE Stage suspend waiting for executing test at @47s +TIMESTAMP INFO Stage start waiting for endpoint config blobset state not defined at @47s +TIMESTAMP is -18078ms +TIMESTAMP DEBUG System config extra field null +TIMESTAMP TRACE Updated check config_system: false +TIMESTAMP TRACE Updated check config_pointset: false +TIMESTAMP TRACE Updated check config_gateway: false +TIMESTAMP TRACE Updated check config_localnet: false +TIMESTAMP TRACE Updated check config_blobset: true +TIMESTAMP DEBUG Update config_blobset, adding configTransaction RC:0162e1.00000013 +TIMESTAMP TRACE Updated check config_discovery: false +TIMESTAMP +TIMESTAMP, because before endpoint config blobset state not defined +TIMESTAMP TRACE Stage suspend waiting for endpoint config blobset state not defined at @47s +TIMESTAMP INFO Stage start waiting for config update synchronized at @47s +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:f715b7f0-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:f715b7f0-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:f715b7f0-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:f715b7f0-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:f715b7f0-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:f715b7f0-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/blobset as MP:f715b7f0-3 +TIMESTAMP TRACE Received state_blobset: { +TIMESTAMP TRACE Received state_blobset: "blobs" : { +TIMESTAMP TRACE Received state_blobset: "_iot_endpoint_config" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "phase" : "final" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP TRACE Received state_blobset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP DEBUG Handling device message state_blobset MP:f715b7f0-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:f715b7f0-4 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:f715b7f0-4 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.00000012 +TIMESTAMP","version":"1.5.5-183-ga8572046b"} +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.00000012 +TIMESTAMP RC:0162e1.00000012 +TIMESTAMP INFO Updated config #011: Changed 1 fields: +TIMESTAMP INFO Updated config #011: Remove `blobset.blobs._iot_endpoint_config` +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: device state not updated since config issued, config transactions not cleared, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/update as CU:0162e1.00000012 +TIMESTAMP DEBUG Received backup message AHU-1/config/update: {"operation":"REPLY"} +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update CU:0162e1.00000012 +TIMESTAMP DEBUG Ignoring operation reply CU:0162e1.00000012 +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:0162e1.00000013 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:0162e1.00000013 +TIMESTAMP DEBUG Removed configTransaction RC:0162e1.00000013 +TIMESTAMP +TIMESTAMP RC:0162e1.00000013 +TIMESTAMP INFO Updated config #012: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: device state not updated since config issued, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/blobset as CU:0162e1.00000013 +TIMESTAMP TRACE Received config_blobset: { +TIMESTAMP TRACE Received config_blobset: "operation" : "REPLY" +TIMESTAMP TRACE Received config_blobset: } +TIMESTAMP DEBUG Handling device message config_blobset CU:0162e1.00000013 +TIMESTAMP TRACE Ignoring echo configTransaction CU:0162e1.00000013 +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:560f0b1d +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:560f0b1d +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:560f0b1d +TIMESTAMP INFO Updated state #013: Changed 3 fields: +TIMESTAMP` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:560f0b1d-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-15295", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:560f0b1d-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @48s after 1s +TIMESTAMP TRACE Stage resume waiting for endpoint config blobset state not defined at @48s +TIMESTAMP DEBUG Config sync took 1s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Recorded sequence: 1. Update config before endpoint config blobset state not defined +TIMESTAMP INFO Device config Remove `blobset.blobs._iot_endpoint_config` +TIMESTAMP TRACE Update config before endpoint config blobset state not defined: Remove `blobset.blobs._iot_endpoint_config` +TIMESTAMP DEBUG Recorded sequence: 1. Wait for endpoint config blobset state not defined +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:560f0b1d-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:560f0b1d-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:560f0b1d-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:560f0b1d-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/blobset as MP:560f0b1d-3 +TIMESTAMP TRACE Received state_blobset: { +TIMESTAMP TRACE Received state_blobset: "blobs" : { +TIMESTAMP TRACE Received state_blobset: "_iot_endpoint_config" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "phase" : "final" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP TRACE Received state_blobset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_blobset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_blobset: } +TIMESTAMP DEBUG Handling device message state_blobset MP:560f0b1d-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:560f0b1d-4 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:560f0b1d-4 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:bec71c82 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:bec71c82 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:bec71c82 +TIMESTAMP INFO Updated state #014: Changed 2 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #014: Remove `blobset` +TIMESTAMP) +TIMESTAMP DEBUG Stage finished waiting for endpoint config blobset state not defined at @51s after 4s +TIMESTAMP TRACE Stage resume waiting for executing test at @51s +TIMESTAMP DEBUG stage done waiting for executing test at @51s +TIMESTAMP DEBUG Removing implicit system capability LAST_CONFIG +TIMESTAMP NOTICE RESULT pass endpoint.config endpoint_connection_download ALPHA 10/10 Sequence complete +TIMESTAMP NOTICE Sequence complete +TIMESTAMP NOTICE Ending test endpoint_connection_download after @51s ################################ diff --git a/validator/sequences/endpoint_connection_download/sequence.md b/validator/sequences/endpoint_connection_download/sequence.md new file mode 100644 index 0000000000..7805558974 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/sequence.md @@ -0,0 +1,13 @@ + +## endpoint_connection_download (ALPHA) + +Check endpoint redirection via download/blobset channel. + +1. Update config before blobset phase is final and stateStatus is null + * Add `blobset` = { "blobs": { "_iot_endpoint_config": { "phase": `final`, "generation": `blob generation`, "sha256": `blob data hash`, "url": `endpoint download url` } } } +1. Wait for blobset phase is final and stateStatus is null +1. Update config before endpoint config blobset state not defined + * Remove `blobset.blobs._iot_endpoint_config` +1. Wait for endpoint config blobset state not defined + +Test passed. diff --git a/validator/sequences/endpoint_connection_download/state_blobset.attr b/validator/sequences/endpoint_connection_download/state_blobset.attr new file mode 100644 index 0000000000..d06d4c9683 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_blobset.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "blobset", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_blobset.json b/validator/sequences/endpoint_connection_download/state_blobset.json new file mode 100644 index 0000000000..1c67823602 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_blobset.json @@ -0,0 +1,10 @@ +{ + "blobs" : { + "_iot_endpoint_config" : { + "generation" : "TIMESTAMP", + "phase" : "final" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_discovery.attr b/validator/sequences/endpoint_connection_download/state_discovery.attr new file mode 100644 index 0000000000..48aa725716 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_discovery.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "discovery", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_discovery.json b/validator/sequences/endpoint_connection_download/state_discovery.json new file mode 100644 index 0000000000..75c8dead1c --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_discovery.json @@ -0,0 +1,5 @@ +{ + "families" : { }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_localnet.attr b/validator/sequences/endpoint_connection_download/state_localnet.attr new file mode 100644 index 0000000000..cf479e3cf5 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_localnet.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "localnet", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_localnet.json b/validator/sequences/endpoint_connection_download/state_localnet.json new file mode 100644 index 0000000000..fc50eaee5e --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_localnet.json @@ -0,0 +1,18 @@ +{ + "families" : { + "bacnet" : { + "addr" : "27312" + }, + "ether" : { + "addr" : "42:01:ac:11:23:ac" + }, + "ipv4" : { + "addr" : "172.17.35.172" + }, + "vendor" : { + "addr" : "28179023" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_pointset.attr b/validator/sequences/endpoint_connection_download/state_pointset.attr new file mode 100644 index 0000000000..7abc4e4f96 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_pointset.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "pointset", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_pointset.json b/validator/sequences/endpoint_connection_download/state_pointset.json new file mode 100644 index 0000000000..1290954444 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_pointset.json @@ -0,0 +1,16 @@ +{ + "points" : { + "filter_alarm_pressure_status" : { + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "units" : "Bars", + "value_state" : "applied" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_system.attr b/validator/sequences/endpoint_connection_download/state_system.attr new file mode 100644 index 0000000000..dc663fe179 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_system.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "system", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_system.json b/validator/sequences/endpoint_connection_download/state_system.json new file mode 100644 index 0000000000..4e10746d47 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_system.json @@ -0,0 +1,26 @@ +{ + "hardware" : { + "make" : "BOS", + "model" : "pubber" + }, + "last_config" : "TIMESTAMP", + "operation" : { + "last_start" : "TIMESTAMP", + "mode" : "initial", + "operational" : true, + "restart_count" : 1 + }, + "serial_no" : "sequencer-15295", + "software" : { + "firmware" : "v1", + "system" : "v1" + }, + "status" : { + "category" : "system.config.apply", + "level" : 300, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_update.attr b/validator/sequences/endpoint_connection_download/state_update.attr new file mode 100644 index 0000000000..4d554d6e6a --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_update.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "update", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_update.json b/validator/sequences/endpoint_connection_download/state_update.json new file mode 100644 index 0000000000..75870ebc7e --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_update.json @@ -0,0 +1,61 @@ +{ + "discovery" : { + "families" : { } + }, + "localnet" : { + "families" : { + "bacnet" : { + "addr" : "27312" + }, + "ether" : { + "addr" : "42:01:ac:11:23:ac" + }, + "ipv4" : { + "addr" : "172.17.35.172" + }, + "vendor" : { + "addr" : "28179023" + } + } + }, + "pointset" : { + "points" : { + "filter_alarm_pressure_status" : { + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "units" : "Bars", + "value_state" : "applied" + } + } + }, + "system" : { + "hardware" : { + "make" : "BOS", + "model" : "pubber" + }, + "last_config" : "TIMESTAMP", + "operation" : { + "last_start" : "TIMESTAMP", + "mode" : "initial", + "operational" : true, + "restart_count" : 1 + }, + "serial_no" : "sequencer-15295", + "software" : { + "firmware" : "v1", + "system" : "v1" + }, + "status" : { + "category" : "system.config.apply", + "level" : 300, + "message" : "success", + "timestamp" : "TIMESTAMP" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_validation.attr b/validator/sequences/endpoint_connection_download/state_validation.attr new file mode 100644 index 0000000000..d52c1d2da2 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_validation.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "validation", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/endpoint_connection_download/state_validation.json b/validator/sequences/endpoint_connection_download/state_validation.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/endpoint_connection_download/state_validation.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_discovery.attr b/validator/sequences/scan_depth_trace/config_discovery.attr new file mode 100644 index 0000000000..80fa7de9d7 --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_discovery.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "discovery", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_discovery.json b/validator/sequences/scan_depth_trace/config_discovery.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_discovery.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_localnet.attr b/validator/sequences/scan_depth_trace/config_localnet.attr new file mode 100644 index 0000000000..982694e31c --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_localnet.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "localnet", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_localnet.json b/validator/sequences/scan_depth_trace/config_localnet.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_localnet.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_pointset.attr b/validator/sequences/scan_depth_trace/config_pointset.attr new file mode 100644 index 0000000000..448196225d --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_pointset.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "pointset", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_pointset.json b/validator/sequences/scan_depth_trace/config_pointset.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_pointset.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_system.attr b/validator/sequences/scan_depth_trace/config_system.attr new file mode 100644 index 0000000000..cd271d7aa4 --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_system.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "system", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_system.json b/validator/sequences/scan_depth_trace/config_system.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_system.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_update.attr b/validator/sequences/scan_depth_trace/config_update.attr new file mode 100644 index 0000000000..e3f64afb75 --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_update.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "update", + "subType" : "config", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/config_update.json b/validator/sequences/scan_depth_trace/config_update.json new file mode 100644 index 0000000000..b8e70445af --- /dev/null +++ b/validator/sequences/scan_depth_trace/config_update.json @@ -0,0 +1,48 @@ +{ + "discovery" : { + "families" : { + "vendor" : { + "depth" : "trace", + "generation" : "TIMESTAMP", + "scan_duration_sec" : 10 + } + } + }, + "localnet" : { + "families" : { + "bacnet" : { }, + "ether" : { }, + "ipv4" : { }, + "vendor" : { } + } + }, + "pointset" : { + "points" : { + "filter_alarm_pressure_status" : { + "ref" : "BV11.present_value", + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "ref" : "AV12.present_value", + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "set_value" : 98, + "units" : "Bars" + } + }, + "sample_rate_sec" : 300 + }, + "system" : { + "metrics_rate_sec" : 10, + "min_loglevel" : 100, + "operation" : { + "last_start" : "TIMESTAMP" + }, + "testing" : { + "sequence_name" : "scan_depth_trace+vendor" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "@@@cloud udmi version###1.5.5-183-ga8572046b" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/device_system.log b/validator/sequences/scan_depth_trace/device_system.log new file mode 100644 index 0000000000..83c7e8290c --- /dev/null +++ b/validator/sequences/scan_depth_trace/device_system.log @@ -0,0 +1,144 @@ +TIMESTAMP DEBUG device.log State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer -6151ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1996ms +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer 1929ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log State update defer 1928ms +TIMESTAMP DEBUG device.log Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1923ms +TIMESTAMP DEBUG device.log State update defer 1922ms +TIMESTAMP DEBUG device.log State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer -79ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG device.log Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1997ms +TIMESTAMP DEBUG device.log State update defer 1996ms +TIMESTAMP DEBUG device.log State update defer 1995ms +TIMESTAMP DEBUG device.log State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer -52ms +TIMESTAMP +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1995ms +TIMESTAMP DEBUG device.log State update defer 215ms +TIMESTAMP DEBUG device.log State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer -49ms +TIMESTAMP +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1996ms +TIMESTAMP DEBUG device.log State update defer 1995ms +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer 1982ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log State update defer 1980ms +TIMESTAMP DEBUG device.log Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1975ms +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer 967ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log State update defer 966ms +TIMESTAMP DEBUG device.log Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 961ms +TIMESTAMP DEBUG device.log State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer 1953ms +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP DEBUG device.log State update defer 1952ms +TIMESTAMP INFO device.log Device AHU-1 config handler +TIMESTAMP DEBUG device.log Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1948ms +TIMESTAMP DEBUG device.log State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG system.config.receive success +TIMESTAMP DEBUG device.log State update defer -4110ms +TIMESTAMP +TIMESTAMP DEBUG system.config.parse success +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG device.log Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP DEBUG device.log value of noWriteback: false +TIMESTAMP +TIMESTAMP DEBUG device.log Scheduling future in 0ms +TIMESTAMP NOTICE system.config.apply success +TIMESTAMP DEBUG device.log State update defer 1993ms +TIMESTAMP DEBUG device.log Scheduling future in 10000ms +TIMESTAMP DEBUG device.log State update defer 1991ms +TIMESTAMP DEBUG device.log State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP) +TIMESTAMP DEBUG device.log State update defer -6009ms +TIMESTAMP +TIMESTAMP DEBUG device.log State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP NOTICE validation.feature.sequence Sequence complete diff --git a/validator/sequences/scan_depth_trace/events_system.attr b/validator/sequences/scan_depth_trace/events_system.attr new file mode 100644 index 0000000000..fe8fc134b0 --- /dev/null +++ b/validator/sequences/scan_depth_trace/events_system.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "system", + "subType" : "events", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/events_system.json b/validator/sequences/scan_depth_trace/events_system.json new file mode 100644 index 0000000000..4c9eb9978e --- /dev/null +++ b/validator/sequences/scan_depth_trace/events_system.json @@ -0,0 +1,32 @@ +{ + "event_no" : 8, + "last_config" : "TIMESTAMP", + "logentries" : [ { + "category" : "device.log", + "level" : 200, + "message" : "Discovered vendor stopping TIMESTAMP)", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 100, + "message" : "State update defer -6009ms", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "level" : 200, + "message" : "Update state TIMESTAMP", + "timestamp" : "TIMESTAMP" + }, { + "category" : "device.log", + "detail" : "{\n \"discovery\" : {\n \"families\" : {\n \"vendor\" : {\n \"active_count\" : 2,\n \"generation\" : \"TIMESTAMP\",\n \"version\" : \"1.5.7\"\n}", + "level" : 100, + "message" : "State update AHU-1 (scan_depth_trace+vendor)", + "timestamp" : "TIMESTAMP" + } ], + "metrics" : { + "mem_free_mb" : 121.9301528930664, + "mem_total_mb" : 160.0 + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/local_discovery.json b/validator/sequences/scan_depth_trace/local_discovery.json new file mode 100644 index 0000000000..ad7e39eb41 --- /dev/null +++ b/validator/sequences/scan_depth_trace/local_discovery.json @@ -0,0 +1,9 @@ +{ + "families" : { + "vendor" : { + "depth" : "trace", + "generation" : "TIMESTAMP", + "scan_duration_sec" : 10 + } + } +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/local_localnet.json b/validator/sequences/scan_depth_trace/local_localnet.json new file mode 100644 index 0000000000..020f9d4dfd --- /dev/null +++ b/validator/sequences/scan_depth_trace/local_localnet.json @@ -0,0 +1,8 @@ +{ + "families" : { + "bacnet" : { }, + "ether" : { }, + "ipv4" : { }, + "vendor" : { } + } +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/local_pointset.json b/validator/sequences/scan_depth_trace/local_pointset.json new file mode 100644 index 0000000000..0180ad7667 --- /dev/null +++ b/validator/sequences/scan_depth_trace/local_pointset.json @@ -0,0 +1,17 @@ +{ + "points" : { + "filter_alarm_pressure_status" : { + "ref" : "BV11.present_value", + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "ref" : "AV12.present_value", + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "set_value" : 98, + "units" : "Bars" + } + }, + "sample_rate_sec" : 300 +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/local_system.json b/validator/sequences/scan_depth_trace/local_system.json new file mode 100644 index 0000000000..c5e1d4ac1d --- /dev/null +++ b/validator/sequences/scan_depth_trace/local_system.json @@ -0,0 +1,10 @@ +{ + "metrics_rate_sec" : 10, + "min_loglevel" : 100, + "operation" : { + "last_start" : "TIMESTAMP" + }, + "testing" : { + "sequence_name" : "scan_depth_trace+vendor" + } +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/local_update.json b/validator/sequences/scan_depth_trace/local_update.json new file mode 100644 index 0000000000..3e96d62428 --- /dev/null +++ b/validator/sequences/scan_depth_trace/local_update.json @@ -0,0 +1,45 @@ +{ + "discovery" : { + "families" : { + "bacnet" : { }, + "vendor" : { } + } + }, + "localnet" : { + "families" : { + "bacnet" : { }, + "ether" : { }, + "ipv4" : { }, + "vendor" : { } + } + }, + "pointset" : { + "points" : { + "filter_alarm_pressure_status" : { + "ref" : "BV11.present_value", + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "ref" : "AV12.present_value", + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "set_value" : 98, + "units" : "Bars" + } + }, + "sample_rate_sec" : 300 + }, + "system" : { + "metrics_rate_sec" : 10, + "min_loglevel" : 100, + "operation" : { + "last_start" : "TIMESTAMP" + }, + "testing" : { + "sequence_name" : "scan_depth_trace+vendor" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "@@@cloud udmi version###1.5.5-183-ga8572046b" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/sequence.log b/validator/sequences/scan_depth_trace/sequence.log new file mode 100644 index 0000000000..caf312e62a --- /dev/null +++ b/validator/sequences/scan_depth_trace/sequence.log @@ -0,0 +1,1697 @@ +TIMESTAMP NOTICE Starting test scan_depth_trace+vendor ################################ +TIMESTAMP NOTICE This test is primary for facet discovery +TIMESTAMP INFO Stage start waiting for starting test wrapper at @0s +TIMESTAMP DEBUG Clear configTransactions and reset device config +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$resetDeviceConfig$14(SequenceBase.java:819) +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Set extraFieldChanged false because extra_field null +TIMESTAMP DEBUG Allowing device state change (everything) +TIMESTAMP DEBUG Starting reset_config full reset true +TIMESTAMP DEBUG Clear configTransactions and reset device config +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$resetDeviceConfig$14(SequenceBase.java:819) +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Set extraFieldChanged false because extra_field null +TIMESTAMP DEBUG Set extraFieldChanged true because extra_field reset_config +TIMESTAMP DEBUG Clear configTransactions and reset device config +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$resetDeviceConfig$14(SequenceBase.java:819) +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Set extraFieldChanged true because extra_field null +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$ensureStateConfigHoldoff$49(SequenceBase.java:1448) +TIMESTAMP is -6013ms +TIMESTAMP TRACE Updated check config_update: true +TIMESTAMP DEBUG Update config_update, adding configTransaction RC:be0b58.0000000a +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP +TIMESTAMP, because soft reset +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @8s +TIMESTAMP INFO Stage start waiting for config update synchronized at @8s +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:7c43508a-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:7c43508a-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:7c43508a-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:7c43508a-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP DEBUG Received command AHU-1/state/validation as RC:be0b58.00000008 +TIMESTAMP TRACE Received state_validation: { +TIMESTAMP TRACE Received state_validation: "operation" : "REPLY" +TIMESTAMP TRACE Received state_validation: } +TIMESTAMP DEBUG Handling device message state_validation RC:be0b58.00000008 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/validation as RC:be0b58.00000009 +TIMESTAMP TRACE Received state_validation: { +TIMESTAMP TRACE Received state_validation: "operation" : "REPLY" +TIMESTAMP TRACE Received state_validation: } +TIMESTAMP DEBUG Handling device message state_validation RC:be0b58.00000009 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:f42b3bc5 +TIMESTAMP DEBUG device.log: State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG Handling device message events_system MP:f42b3bc5 +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$105(SequenceBase.java:2310) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "system" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$107(SequenceBase.java:2314) +TIMESTAMP TRACE Suppressing exception: java.lang.NullPointerException: Cannot read field "timestamp" because "this.deviceState" is null, at com.google.daq.mqtt.sequencer.SequenceBase.lambda$configIsPending$109(SequenceBase.java:2321) +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:be0b58.0000000a +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:be0b58.0000000a +TIMESTAMP DEBUG Removed configTransaction RC:be0b58.0000000a +TIMESTAMP +TIMESTAMP RC:be0b58.0000000a +TIMESTAMP INFO Updated config #005: Changed 1 fields: +TIMESTAMP INFO Updated config #005: Set `system.testing.sequence_name` = `scan_depth_trace+vendor` +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:22125cd4 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:22125cd4 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:22125cd4 +TIMESTAMP INFO Updated state #007: Changed 4 fields: +TIMESTAMP INFO Updated state #007: Set `system.status.level` = `100` +TIMESTAMP INFO Updated state #007: Set `system.status.category` = `system.config.parse` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP NOTICE Received serial number sequencer-12734 +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: last_start not synced in config, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/update as CU:be0b58.0000000a +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update CU:be0b58.0000000a +TIMESTAMP DEBUG Ignoring operation reply CU:be0b58.0000000a +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:22125cd4-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.parse", +TIMESTAMP TRACE Received state_system: "level" : 100, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:22125cd4-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:22125cd4-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:22125cd4-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:22125cd4-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:22125cd4-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:22125cd4-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:22125cd4-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as MP:22125cd4 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update MP:22125cd4 +TIMESTAMP +TIMESTAMP MP:22125cd4 +TIMESTAMP INFO Updated config #006: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:28f641e5 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:28f641e5 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:28f641e5 +TIMESTAMP INFO Updated state #008: Changed 4 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #008: Set `system.status.level` = `300` +TIMESTAMP INFO Updated state #008: Set `system.status.category` = `system.config.apply` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @10s after 2s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @10s +TIMESTAMP DEBUG Config sync took 2s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP +TIMESTAMP +TIMESTAMP TRACE Update config soft reset: Add `discovery` = { "families": { "vendor": { }, "bacnet": { } } } +TIMESTAMP TRACE Update config soft reset: Add `pointset` = { "sample_rate_sec": `300`, "points": { "filter_differential_pressure_setpoint": { "set_value": `98`, "units": `Bars` }, "filter_alarm_pressure_status": { "ref": `BV11.present_value`, "units": `No-units` }, "filter_differential_pressure_sensor": { "ref": `AV12.present_value`, "units": `Degrees-Celsius` } } } +TIMESTAMP TRACE Update config soft reset: Add `localnet` = { "families": { "ether": { }, "ipv4": { }, "vendor": { }, "bacnet": { } } } +TIMESTAMP TRACE Update config soft reset: Add `timestamp` = `generated timestamp` +TIMESTAMP DEBUG Done with reset_config +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @10s +TIMESTAMP INFO Stage start waiting for config update synchronized at @10s +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:28f641e5-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:28f641e5-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:28f641e5-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:28f641e5-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @10s after 0s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @10s +TIMESTAMP DEBUG Config sync took 2s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP DEBUG Disallowing device state change (everything) +TIMESTAMP is 1326ms +TIMESTAMP DEBUG Rate-limiting config by 1326ms +TIMESTAMP TRACE Updated check config_update: true +TIMESTAMP DEBUG Update config_update, adding configTransaction RC:be0b58.0000000b +TIMESTAMP +TIMESTAMP, because initial setup +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @12s +TIMESTAMP INFO Stage start waiting for config update synchronized at @12s +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:28f641e5-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:28f641e5-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:28f641e5-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:28f641e5-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:be0b58.0000000b +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:be0b58.0000000b +TIMESTAMP DEBUG Removed configTransaction RC:be0b58.0000000b +TIMESTAMP +TIMESTAMP RC:be0b58.0000000b +TIMESTAMP INFO Updated config #007: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: device state not updated since config issued, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:653dd657 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:653dd657 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:653dd657 +TIMESTAMP INFO Updated state #009: Changed 4 fields: +TIMESTAMP INFO Updated state #009: Set `system.status.level` = `100` +TIMESTAMP INFO Updated state #009: Set `system.status.category` = `system.config.parse` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Received command AHU-1/config/update as CU:be0b58.0000000b +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update CU:be0b58.0000000b +TIMESTAMP DEBUG Ignoring operation reply CU:be0b58.0000000b +TIMESTAMP DEBUG Detail config update synchronized is now: last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:653dd657-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.parse", +TIMESTAMP TRACE Received state_system: "level" : 100, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:653dd657-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:653dd657-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:653dd657-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:653dd657-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:653dd657-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:653dd657-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:653dd657-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:b77ec59d +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:b77ec59d +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:b77ec59d +TIMESTAMP INFO Updated state #010: Changed 4 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #010: Set `system.status.level` = `300` +TIMESTAMP INFO Updated state #010: Set `system.status.category` = `system.config.apply` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @14s after 2s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @14s +TIMESTAMP DEBUG Config sync took 2s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Waiting for device stateTransaction RC:be0b58.0000000d (was null) +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @14s +TIMESTAMP INFO Stage start waiting for state query at @14s +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:b77ec59d-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:b77ec59d-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:b77ec59d-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:b77ec59d-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:b77ec59d-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:b77ec59d-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:b77ec59d-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:b77ec59d-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/update as RC:be0b58.0000000d +TIMESTAMP DEBUG Received state_update +TIMESTAMP INFO Received device configAcked +TIMESTAMP DEBUG Handling update message state_update RC:be0b58.0000000d +TIMESTAMP DEBUG Removed stateTransaction RC:be0b58.0000000d +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP RC:be0b58.0000000d +TIMESTAMP INFO Updated state #011: Changed 0 fields. +TIMESTAMP) +TIMESTAMP DEBUG Stage finished waiting for state query at @15s after 1s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @15s +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @15s +TIMESTAMP INFO Stage start waiting for initial device state at @15s +TIMESTAMP is 975ms +TIMESTAMP DEBUG Rate-limiting config by 975ms +TIMESTAMP TRACE Updated check config_update: true +TIMESTAMP DEBUG Update config_update, adding configTransaction RC:be0b58.0000000e +TIMESTAMP +TIMESTAMP, because before initial device state +TIMESTAMP TRACE Stage suspend waiting for initial device state at @16s +TIMESTAMP INFO Stage start waiting for config update synchronized at @16s +TIMESTAMP DEBUG Received command AHU-1/reply/update as RC:be0b58.0000000d +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:be0b58.0000000e +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:be0b58.0000000e +TIMESTAMP DEBUG Removed configTransaction RC:be0b58.0000000e +TIMESTAMP +TIMESTAMP RC:be0b58.0000000e +TIMESTAMP INFO Updated config #008: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:e58c1c46 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:e58c1c46 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:e58c1c46 +TIMESTAMP INFO Updated state #012: Changed 4 fields: +TIMESTAMP INFO Updated state #012: Set `system.status.level` = `100` +TIMESTAMP INFO Updated state #012: Set `system.status.category` = `system.config.parse` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/update as CU:be0b58.0000000e +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update CU:be0b58.0000000e +TIMESTAMP DEBUG Ignoring operation reply CU:be0b58.0000000e +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:e58c1c46-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.parse", +TIMESTAMP TRACE Received state_system: "level" : 100, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:e58c1c46-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:e58c1c46-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:e58c1c46-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:e58c1c46-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:e58c1c46-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:e58c1c46-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:e58c1c46-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:3644cd2e +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer -6151ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (endpoint_connection_download) +TIMESTAMP DEBUG device.log: Config update AHU-1 (endpoint_connection_download) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1996ms +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer 1929ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: State update defer 1928ms +TIMESTAMP DEBUG device.log: Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1923ms +TIMESTAMP DEBUG device.log: State update defer 1922ms +TIMESTAMP DEBUG device.log: State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer -79ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG device.log: Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1997ms +TIMESTAMP DEBUG device.log: State update defer 1996ms +TIMESTAMP DEBUG device.log: State update defer 1995ms +TIMESTAMP DEBUG device.log: State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer -52ms +TIMESTAMP +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1995ms +TIMESTAMP DEBUG device.log: State update defer 215ms +TIMESTAMP DEBUG Handling device message events_system MP:3644cd2e +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:64718ef9 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:64718ef9 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:64718ef9 +TIMESTAMP INFO Updated state #013: Changed 4 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #013: Set `system.status.level` = `300` +TIMESTAMP INFO Updated state #013: Set `system.status.category` = `system.config.apply` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @18s after 2s +TIMESTAMP TRACE Stage resume waiting for initial device state at @18s +TIMESTAMP DEBUG Config sync took 2s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Stage finished waiting for initial device state at @18s after 3s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @18s +TIMESTAMP DEBUG Device state system status level is now 300 +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @18s +TIMESTAMP INFO Stage start waiting for config update synchronized at @18s +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:64718ef9-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:64718ef9-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @19s after 1s +TIMESTAMP TRACE Stage resume waiting for starting test wrapper at @19s +TIMESTAMP DEBUG Config sync took 3s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP TRACE Stage suspend waiting for starting test wrapper at @19s +TIMESTAMP INFO Stage start waiting for executing test at @19s +TIMESTAMP DEBUG stage begin waiting for executing test at @19s +TIMESTAMP DEBUG Allowing device state change discovery +TIMESTAMP TRACE Stage suspend waiting for executing test at @19s +TIMESTAMP INFO Stage start waiting for discovery families defined at @19s +TIMESTAMP is 990ms +TIMESTAMP DEBUG Rate-limiting config by 990ms +TIMESTAMP DEBUG System config extra field null +TIMESTAMP TRACE Updated check config_system: true +TIMESTAMP DEBUG Update config_system, adding configTransaction RC:be0b58.0000000f +TIMESTAMP TRACE Updated check config_pointset: true +TIMESTAMP DEBUG Update config_pointset, adding configTransaction RC:be0b58.00000010 +TIMESTAMP TRACE Updated check config_gateway: false +TIMESTAMP TRACE Updated check config_localnet: true +TIMESTAMP DEBUG Update config_localnet, adding configTransaction RC:be0b58.00000011 +TIMESTAMP TRACE Updated check config_blobset: false +TIMESTAMP TRACE Updated check config_discovery: true +TIMESTAMP DEBUG Update config_discovery, adding configTransaction RC:be0b58.00000012 +TIMESTAMP +TIMESTAMP, because before discovery families defined +TIMESTAMP TRACE Stage suspend waiting for discovery families defined at @20s +TIMESTAMP INFO Stage start waiting for config update synchronized at @20s +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:64718ef9-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:64718ef9-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:64718ef9-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:64718ef9-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:64718ef9-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:64718ef9-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:be0b58.0000000f +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:be0b58.0000000f +TIMESTAMP DEBUG Removed configTransaction RC:be0b58.0000000f +TIMESTAMP +TIMESTAMP RC:be0b58.0000000f +TIMESTAMP INFO Updated config #009: Changed 2 fields: +TIMESTAMP INFO Updated config #009: Remove `discovery.families.vendor` +TIMESTAMP INFO Updated config #009: Remove `discovery.families.bacnet` +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: device state not updated since config issued, config transactions not cleared, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/system as CU:be0b58.0000000f +TIMESTAMP TRACE Received config_system: { +TIMESTAMP TRACE Received config_system: "operation" : "REPLY" +TIMESTAMP TRACE Received config_system: } +TIMESTAMP DEBUG Handling device message config_system CU:be0b58.0000000f +TIMESTAMP TRACE Ignoring echo configTransaction CU:be0b58.0000000f +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:81df6f77 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:81df6f77 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:81df6f77 +TIMESTAMP INFO Updated state #014: Changed 4 fields: +TIMESTAMP INFO Updated state #014: Set `system.status.level` = `100` +TIMESTAMP INFO Updated state #014: Set `system.status.category` = `system.config.parse` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:be0b58.00000010 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:be0b58.00000010 +TIMESTAMP DEBUG Removed configTransaction RC:be0b58.00000010 +TIMESTAMP +TIMESTAMP RC:be0b58.00000010 +TIMESTAMP INFO Updated config #010: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: config transactions not cleared, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/pointset as CU:be0b58.00000010 +TIMESTAMP TRACE Received config_pointset: { +TIMESTAMP TRACE Received config_pointset: "operation" : "REPLY" +TIMESTAMP TRACE Received config_pointset: } +TIMESTAMP DEBUG Handling device message config_pointset CU:be0b58.00000010 +TIMESTAMP TRACE Ignoring echo configTransaction CU:be0b58.00000010 +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:81df6f77-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.parse", +TIMESTAMP TRACE Received state_system: "level" : 100, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:81df6f77-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:81df6f77-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:81df6f77-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:81df6f77-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:81df6f77-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:81df6f77-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:81df6f77-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:be0b58.00000011 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:be0b58.00000011 +TIMESTAMP DEBUG Removed configTransaction RC:be0b58.00000011 +TIMESTAMP +TIMESTAMP RC:be0b58.00000011 +TIMESTAMP INFO Updated config #011: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/config/localnet as CU:be0b58.00000011 +TIMESTAMP TRACE Received config_localnet: { +TIMESTAMP TRACE Received config_localnet: "operation" : "REPLY" +TIMESTAMP TRACE Received config_localnet: } +TIMESTAMP DEBUG Handling device message config_localnet CU:be0b58.00000011 +TIMESTAMP TRACE Ignoring echo configTransaction CU:be0b58.00000011 +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:072a2602 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:072a2602 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:072a2602 +TIMESTAMP INFO Updated state #015: Changed 5 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #015: Set `system.status.level` = `300` +TIMESTAMP INFO Updated state #015: Set `system.status.category` = `system.config.apply` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:be0b58.00000012 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:be0b58.00000012 +TIMESTAMP DEBUG Removed configTransaction RC:be0b58.00000012 +TIMESTAMP +TIMESTAMP RC:be0b58.00000012 +TIMESTAMP INFO Updated config #012: Changed 0 fields. +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/config/discovery as CU:be0b58.00000012 +TIMESTAMP TRACE Received config_discovery: { +TIMESTAMP TRACE Received config_discovery: "operation" : "REPLY" +TIMESTAMP TRACE Received config_discovery: } +TIMESTAMP DEBUG Handling device message config_discovery CU:be0b58.00000012 +TIMESTAMP TRACE Ignoring echo configTransaction CU:be0b58.00000012 +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:072a2602-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:072a2602-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:072a2602-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:072a2602-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:072a2602-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:072a2602-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:072a2602-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:072a2602-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:039031ee +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:039031ee +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:039031ee +TIMESTAMP INFO Updated state #016: Changed 3 fields: +TIMESTAMP` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @24s after 4s +TIMESTAMP TRACE Stage resume waiting for discovery families defined at @24s +TIMESTAMP DEBUG Config sync took 4s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Recorded sequence: 1. Update config before discovery families defined +TIMESTAMP INFO Device config Remove `discovery.families.vendor` +TIMESTAMP INFO Device config Remove `discovery.families.bacnet` +TIMESTAMP TRACE Update config before discovery families defined: Remove `discovery.families.vendor` +TIMESTAMP TRACE Update config before discovery families defined: Remove `discovery.families.bacnet` +TIMESTAMP DEBUG Recorded sequence: 1. Wait for discovery families defined +TIMESTAMP DEBUG Stage finished waiting for discovery families defined at @24s after 5s +TIMESTAMP TRACE Stage resume waiting for executing test at @24s +TIMESTAMP TRACE Stage suspend waiting for executing test at @24s +TIMESTAMP INFO Stage start waiting for discovery family keys match at @24s +TIMESTAMP is 1575ms +TIMESTAMP DEBUG Rate-limiting config by 1575ms +TIMESTAMP DEBUG System config extra field null +TIMESTAMP TRACE Updated check config_system: false +TIMESTAMP TRACE Updated check config_pointset: false +TIMESTAMP TRACE Updated check config_gateway: false +TIMESTAMP TRACE Updated check config_localnet: false +TIMESTAMP TRACE Updated check config_blobset: false +TIMESTAMP TRACE Updated check config_discovery: false +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Recorded sequence: 1. Wait until discovery family keys match +TIMESTAMP DEBUG Stage finished waiting for discovery family keys match at @26s after 2s +TIMESTAMP TRACE Stage resume waiting for executing test at @26s +TIMESTAMP TRACE Stage suspend waiting for executing test at @26s +TIMESTAMP INFO Stage start waiting for no scans active at @26s +TIMESTAMP is 1997ms +TIMESTAMP DEBUG Rate-limiting config by 1997ms +TIMESTAMP DEBUG System config extra field null +TIMESTAMP TRACE Updated check config_system: false +TIMESTAMP TRACE Updated check config_pointset: false +TIMESTAMP TRACE Updated check config_gateway: false +TIMESTAMP TRACE Updated check config_localnet: false +TIMESTAMP TRACE Updated check config_blobset: false +TIMESTAMP TRACE Updated check config_discovery: false +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Recorded sequence: 1. Wait for no scans active +TIMESTAMP DEBUG Stage finished waiting for no scans active at @28s after 2s +TIMESTAMP TRACE Stage resume waiting for executing test at @28s +TIMESTAMP evey nulls +TIMESTAMP TRACE Stage suspend waiting for executing test at @28s +TIMESTAMP INFO Stage start waiting for scheduled scan active at @28s +TIMESTAMP is 1994ms +TIMESTAMP DEBUG Rate-limiting config by 1994ms +TIMESTAMP DEBUG System config extra field null +TIMESTAMP TRACE Updated check config_system: false +TIMESTAMP TRACE Updated check config_pointset: false +TIMESTAMP TRACE Updated check config_gateway: false +TIMESTAMP TRACE Updated check config_localnet: false +TIMESTAMP TRACE Updated check config_blobset: false +TIMESTAMP TRACE Updated check config_discovery: true +TIMESTAMP DEBUG Update config_discovery, adding configTransaction RC:be0b58.00000013 +TIMESTAMP +TIMESTAMP, because before scheduled scan active +TIMESTAMP TRACE Stage suspend waiting for scheduled scan active at @30s +TIMESTAMP INFO Stage start waiting for config update synchronized at @30s +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:039031ee-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:039031ee-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:039031ee-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:039031ee-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:039031ee-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:039031ee-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:039031ee-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:039031ee-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command null/config/udmi as null +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:1efcee34 +TIMESTAMP DEBUG device.log: State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer -49ms +TIMESTAMP +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1996ms +TIMESTAMP DEBUG device.log: State update defer 1995ms +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer 1982ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: State update defer 1980ms +TIMESTAMP DEBUG device.log: Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1975ms +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer 967ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: State update defer 966ms +TIMESTAMP DEBUG device.log: Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 961ms +TIMESTAMP DEBUG device.log: State update defer 0ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer 1953ms +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP DEBUG device.log: State update defer 1952ms +TIMESTAMP INFO device.log: Device AHU-1 config handler +TIMESTAMP DEBUG device.log: Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1948ms +TIMESTAMP DEBUG device.log: State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG Handling device message events_system MP:1efcee34 +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP DEBUG Received command AHU-1/config/update as RC:be0b58.00000013 +TIMESTAMP DEBUG Received config_update +TIMESTAMP DEBUG Handling update message config_update RC:be0b58.00000013 +TIMESTAMP DEBUG Removed configTransaction RC:be0b58.00000013 +TIMESTAMP +TIMESTAMP RC:be0b58.00000013 +TIMESTAMP INFO Updated config #013: Changed 1 fields: +TIMESTAMP INFO Updated config #013: Add `discovery.families.vendor` = { "generation": `family generation`, "depth": `trace`, "scan_duration_sec": `10` } +TIMESTAMP +TIMESTAMP DEBUG Detail config update synchronized is now: device state not updated since config issued, last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:1c2b7725 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:1c2b7725 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:1c2b7725 +TIMESTAMP INFO Updated state #017: Changed 4 fields: +TIMESTAMP INFO Updated state #017: Set `system.status.level` = `100` +TIMESTAMP INFO Updated state #017: Set `system.status.category` = `system.config.parse` +TIMESTAMP` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Received command AHU-1/config/discovery as CU:be0b58.00000013 +TIMESTAMP TRACE Received config_discovery: { +TIMESTAMP TRACE Received config_discovery: "operation" : "REPLY" +TIMESTAMP TRACE Received config_discovery: } +TIMESTAMP DEBUG Handling device message config_discovery CU:be0b58.00000013 +TIMESTAMP TRACE Ignoring echo configTransaction CU:be0b58.00000013 +TIMESTAMP DEBUG Detail config update synchronized is now: last_config not synced in state +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:1c2b7725-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.parse", +TIMESTAMP TRACE Received state_system: "level" : 100, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:1c2b7725-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:1c2b7725-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:1c2b7725-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:1c2b7725-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:1c2b7725-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:1c2b7725-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:1c2b7725-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:b05927f4 +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:b05927f4 +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:b05927f4 +TIMESTAMP INFO Updated state #018: Changed 5 fields: +TIMESTAMP` +TIMESTAMP INFO Updated state #018: Set `system.status.level` = `300` +TIMESTAMP INFO Updated state #018: Set `system.status.category` = `system.config.apply` +TIMESTAMP`, "phase": `active`, "active_count": `0` } +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail config update synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for config update synchronized at @32s after 2s +TIMESTAMP TRACE Stage resume waiting for scheduled scan active at @32s +TIMESTAMP DEBUG Config sync took 2s +TIMESTAMP DEBUG Finished wait for config sync pending: null +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Recorded sequence: 1. Update config before scheduled scan active +TIMESTAMP INFO Device config Add `discovery.families.vendor` = { "generation": `family generation`, "depth": `trace`, "scan_duration_sec": `10` } +TIMESTAMP TRACE Update config before scheduled scan active: Add `discovery.families.vendor` = { "generation": `family generation`, "depth": `trace`, "scan_duration_sec": `10` } +TIMESTAMP DEBUG Recorded sequence: 1. Wait until scheduled scan active +TIMESTAMP DEBUG Stage finished waiting for scheduled scan active at @32s after 4s +TIMESTAMP TRACE Stage resume waiting for executing test at @32s +TIMESTAMP DEBUG Recorded sequence: 1. Check that scan started at time +TIMESTAMP TRACE Stage suspend waiting for executing test at @32s +TIMESTAMP INFO Stage start waiting for scheduled scan complete at @32s +TIMESTAMP is 1656ms +TIMESTAMP DEBUG Rate-limiting config by 1656ms +TIMESTAMP DEBUG System config extra field null +TIMESTAMP TRACE Updated check config_system: false +TIMESTAMP TRACE Updated check config_pointset: false +TIMESTAMP TRACE Updated check config_gateway: false +TIMESTAMP TRACE Updated check config_localnet: false +TIMESTAMP TRACE Updated check config_blobset: false +TIMESTAMP TRACE Updated check config_discovery: false +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:b05927f4-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:b05927f4-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:b05927f4-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { +TIMESTAMP TRACE Received state_discovery: "vendor" : { +TIMESTAMP TRACE Received state_discovery: "active_count" : 0, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "phase" : "active" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP TRACE Received state_discovery: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:b05927f4-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:b05927f4-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:b05927f4-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:b05927f4-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:b05927f4-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:682a5ef4 +TIMESTAMP DEBUG system.config.receive: success +TIMESTAMP DEBUG device.log: State update defer -4110ms +TIMESTAMP +TIMESTAMP DEBUG system.config.parse: success +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG device.log: Config update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP DEBUG device.log: value of noWriteback: false +TIMESTAMP +TIMESTAMP DEBUG device.log: Scheduling future in 0ms +TIMESTAMP NOTICE system.config.apply: success +TIMESTAMP DEBUG device.log: State update defer 1993ms +TIMESTAMP DEBUG device.log: Scheduling future in 10000ms +TIMESTAMP DEBUG device.log: State update defer 1991ms +TIMESTAMP DEBUG device.log: State update defer -1ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG Handling device message events_system MP:682a5ef4 +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP DEBUG Received command AHU-1/upload/discovery as MP:f11f7e0e +TIMESTAMP DEBUG Received upload_discovery +TIMESTAMP DEBUG Handling device message upload_discovery MP:f11f7e0e +TIMESTAMP DEBUG Capturing AHU-1 message upload_discovery +TIMESTAMP DEBUG Received command AHU-1/upload/discovery as MP:4fd1422a +TIMESTAMP DEBUG Received upload_discovery +TIMESTAMP DEBUG Handling device message upload_discovery MP:4fd1422a +TIMESTAMP DEBUG Capturing AHU-1 message upload_discovery +TIMESTAMP DEBUG Received command AHU-1/upload/discovery as MP:7fde77b3 +TIMESTAMP DEBUG Received upload_discovery +TIMESTAMP DEBUG Handling device message upload_discovery MP:7fde77b3 +TIMESTAMP DEBUG Capturing AHU-1 message upload_discovery +TIMESTAMP DEBUG Received command AHU-1/upload/discovery as MP:a3ed769c +TIMESTAMP DEBUG Received upload_discovery +TIMESTAMP DEBUG Handling device message upload_discovery MP:a3ed769c +TIMESTAMP DEBUG Capturing AHU-1 message upload_discovery +TIMESTAMP DEBUG Received command AHU-1/state/update as MP:697b721c +TIMESTAMP DEBUG Received state_update +TIMESTAMP DEBUG Handling update message state_update MP:697b721c +TIMESTAMP DEBUG Capturing AHU-1 message state_update +TIMESTAMP MP:697b721c +TIMESTAMP INFO Updated state #019: Changed 3 fields: +TIMESTAMP INFO Updated state #019: Set `discovery.families.vendor.phase` = `stopped` +TIMESTAMP INFO Updated state #019: Set `discovery.families.vendor.active_count` = `2` +TIMESTAMP` +TIMESTAMP) +TIMESTAMP DEBUG Detail scheduled scan complete is now: null +TIMESTAMP DEBUG Recorded sequence: 1. Wait until scheduled scan complete +TIMESTAMP DEBUG Stage finished waiting for scheduled scan complete at @40s after 8s +TIMESTAMP TRACE Stage resume waiting for executing test at @40s +TIMESTAMP DEBUG Recorded sequence: 1. Check that scan completed at time +TIMESTAMP DEBUG Recorded sequence: 1. Check that trace scan reported positive active_count +TIMESTAMP TRACE Stage suspend waiting for executing test at @40s +TIMESTAMP INFO Stage start waiting for sleeping 10s for discovery uploads synchronized at @40s +TIMESTAMP is -4342ms +TIMESTAMP DEBUG System config extra field null +TIMESTAMP TRACE Updated check config_system: false +TIMESTAMP TRACE Updated check config_pointset: false +TIMESTAMP TRACE Updated check config_gateway: false +TIMESTAMP TRACE Updated check config_localnet: false +TIMESTAMP TRACE Updated check config_blobset: false +TIMESTAMP TRACE Updated check config_discovery: false +TIMESTAMP +TIMESTAMP +TIMESTAMP DEBUG Received command AHU-1/state/system as MP:697b721c-0 +TIMESTAMP TRACE Received state_system: { +TIMESTAMP TRACE Received state_system: "hardware" : { +TIMESTAMP TRACE Received state_system: "make" : "BOS", +TIMESTAMP TRACE Received state_system: "model" : "pubber" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "operation" : { +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "mode" : "initial", +TIMESTAMP TRACE Received state_system: "operational" : true, +TIMESTAMP TRACE Received state_system: "restart_count" : 1 +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "serial_no" : "sequencer-12734", +TIMESTAMP TRACE Received state_system: "software" : { +TIMESTAMP TRACE Received state_system: "firmware" : "v1", +TIMESTAMP TRACE Received state_system: "system" : "v1" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP TRACE Received state_system: "status" : { +TIMESTAMP TRACE Received state_system: "category" : "system.config.apply", +TIMESTAMP TRACE Received state_system: "level" : 300, +TIMESTAMP TRACE Received state_system: "message" : "success", +TIMESTAMP" +TIMESTAMP TRACE Received state_system: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_system: "version" : "1.5.7" +TIMESTAMP TRACE Received state_system: } +TIMESTAMP DEBUG Handling device message state_system MP:697b721c-0 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/discovery as MP:697b721c-1 +TIMESTAMP TRACE Received state_discovery: { +TIMESTAMP TRACE Received state_discovery: "families" : { +TIMESTAMP TRACE Received state_discovery: "vendor" : { +TIMESTAMP TRACE Received state_discovery: "active_count" : 2, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "phase" : "stopped" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP TRACE Received state_discovery: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_discovery: "version" : "1.5.7" +TIMESTAMP TRACE Received state_discovery: } +TIMESTAMP DEBUG Handling device message state_discovery MP:697b721c-1 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/localnet as MP:697b721c-2 +TIMESTAMP TRACE Received state_localnet: { +TIMESTAMP TRACE Received state_localnet: "families" : { +TIMESTAMP TRACE Received state_localnet: "bacnet" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "27312" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ether" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "42:01:ac:11:23:ac" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "ipv4" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "172.17.35.172" +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP TRACE Received state_localnet: "vendor" : { +TIMESTAMP TRACE Received state_localnet: "addr" : "28179023" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP TRACE Received state_localnet: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_localnet: "version" : "1.5.7" +TIMESTAMP TRACE Received state_localnet: } +TIMESTAMP DEBUG Handling device message state_localnet MP:697b721c-2 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/state/pointset as MP:697b721c-3 +TIMESTAMP TRACE Received state_pointset: { +TIMESTAMP TRACE Received state_pointset: "points" : { +TIMESTAMP TRACE Received state_pointset: "filter_alarm_pressure_status" : { +TIMESTAMP TRACE Received state_pointset: "units" : "No-units" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_sensor" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Degrees-Celsius" +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP TRACE Received state_pointset: "filter_differential_pressure_setpoint" : { +TIMESTAMP TRACE Received state_pointset: "units" : "Bars", +TIMESTAMP TRACE Received state_pointset: "value_state" : "applied" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP TRACE Received state_pointset: }, +TIMESTAMP", +TIMESTAMP TRACE Received state_pointset: "version" : "1.5.7" +TIMESTAMP TRACE Received state_pointset: } +TIMESTAMP DEBUG Handling device message state_pointset MP:697b721c-3 +TIMESTAMP TRACE Ignoring partial state update +TIMESTAMP DEBUG Received command AHU-1/events/system as MP:ffff3f5f +TIMESTAMP) +TIMESTAMP DEBUG device.log: State update defer -6009ms +TIMESTAMP +TIMESTAMP DEBUG device.log: State update AHU-1 (scan_depth_trace+vendor) +TIMESTAMP DEBUG Handling device message events_system MP:ffff3f5f +TIMESTAMP DEBUG Capturing AHU-1 message events_system +TIMESTAMP DEBUG Detail sleeping 10s for discovery uploads synchronized is now: null +TIMESTAMP DEBUG Stage finished waiting for sleeping 10s for discovery uploads synchronized at @50s after 10s +TIMESTAMP TRACE Stage resume waiting for executing test at @50s +TIMESTAMP DEBUG Recorded sequence: 1. Check that no discovery events emitted during trace scan +TIMESTAMP DEBUG Recorded sequence: 1. Check that received expected number of discovery uploads +TIMESTAMP DEBUG Recorded sequence: 1. Check that all discovery uploads match scan family and generation +TIMESTAMP DEBUG Recorded sequence: 1. Check that received all unique upload event numbers +TIMESTAMP DEBUG Recorded sequence: 1. Check that received proper upload start marker +TIMESTAMP DEBUG Recorded sequence: 1. Check that received proper last upload chunk +TIMESTAMP DEBUG Recorded sequence: 1. Check that received proper upload termination marker +TIMESTAMP DEBUG Recorded sequence: 1. Check that upload chunk 1 data present +TIMESTAMP DEBUG Recorded sequence: 1. Check that upload chunk 2 data present +TIMESTAMP DEBUG Recorded sequence: 1. Check that upload termination marker includes sha256 +TIMESTAMP DEBUG Recorded sequence: 1. Check that assembled upload matches termination marker sha256 +TIMESTAMP DEBUG stage done waiting for executing test at @50s +TIMESTAMP DEBUG Removing implicit system capability LAST_CONFIG +TIMESTAMP NOTICE RESULT pass discovery.scan scan_depth_trace+vendor ALPHA 10/10 Sequence complete +TIMESTAMP NOTICE Sequence complete +TIMESTAMP NOTICE Ending test scan_depth_trace+vendor after @50s ################################ diff --git a/validator/sequences/scan_depth_trace/sequence.md b/validator/sequences/scan_depth_trace/sequence.md new file mode 100644 index 0000000000..ef8e02ec87 --- /dev/null +++ b/validator/sequences/scan_depth_trace/sequence.md @@ -0,0 +1,31 @@ + +## scan_depth_trace+vendor (ALPHA) + +Check results of a single scan with trace depth + +1. Update config before discovery families defined + * Remove `discovery.families.vendor` + * Remove `discovery.families.bacnet` +1. Wait for discovery families defined +1. Wait until discovery family keys match +1. Wait for no scans active +1. Update config before scheduled scan active + * Add `discovery.families.vendor` = { "generation": `family generation`, "depth": `trace`, "scan_duration_sec": `10` } +1. Wait until scheduled scan active +1. Check that scan started at time +1. Wait until scheduled scan complete +1. Check that scan completed at time +1. Check that trace scan reported positive active_count +1. Check that no discovery events emitted during trace scan +1. Check that received expected number of discovery uploads +1. Check that all discovery uploads match scan family and generation +1. Check that received all unique upload event numbers +1. Check that received proper upload start marker +1. Check that received proper last upload chunk +1. Check that received proper upload termination marker +1. Check that upload chunk 1 data present +1. Check that upload chunk 2 data present +1. Check that upload termination marker includes sha256 +1. Check that assembled upload matches termination marker sha256 + +Test passed. diff --git a/validator/sequences/scan_depth_trace/state_discovery.attr b/validator/sequences/scan_depth_trace/state_discovery.attr new file mode 100644 index 0000000000..48aa725716 --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_discovery.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "discovery", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_discovery.json b/validator/sequences/scan_depth_trace/state_discovery.json new file mode 100644 index 0000000000..4c51bd7ae5 --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_discovery.json @@ -0,0 +1,11 @@ +{ + "families" : { + "vendor" : { + "active_count" : 2, + "generation" : "TIMESTAMP", + "phase" : "stopped" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_localnet.attr b/validator/sequences/scan_depth_trace/state_localnet.attr new file mode 100644 index 0000000000..cf479e3cf5 --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_localnet.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "localnet", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_localnet.json b/validator/sequences/scan_depth_trace/state_localnet.json new file mode 100644 index 0000000000..fc50eaee5e --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_localnet.json @@ -0,0 +1,18 @@ +{ + "families" : { + "bacnet" : { + "addr" : "27312" + }, + "ether" : { + "addr" : "42:01:ac:11:23:ac" + }, + "ipv4" : { + "addr" : "172.17.35.172" + }, + "vendor" : { + "addr" : "28179023" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_pointset.attr b/validator/sequences/scan_depth_trace/state_pointset.attr new file mode 100644 index 0000000000..7abc4e4f96 --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_pointset.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "pointset", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_pointset.json b/validator/sequences/scan_depth_trace/state_pointset.json new file mode 100644 index 0000000000..1290954444 --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_pointset.json @@ -0,0 +1,16 @@ +{ + "points" : { + "filter_alarm_pressure_status" : { + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "units" : "Bars", + "value_state" : "applied" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_system.attr b/validator/sequences/scan_depth_trace/state_system.attr new file mode 100644 index 0000000000..dc663fe179 --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_system.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "system", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_system.json b/validator/sequences/scan_depth_trace/state_system.json new file mode 100644 index 0000000000..5d5ac2f20e --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_system.json @@ -0,0 +1,26 @@ +{ + "hardware" : { + "make" : "BOS", + "model" : "pubber" + }, + "last_config" : "TIMESTAMP", + "operation" : { + "last_start" : "TIMESTAMP", + "mode" : "initial", + "operational" : true, + "restart_count" : 1 + }, + "serial_no" : "sequencer-12734", + "software" : { + "firmware" : "v1", + "system" : "v1" + }, + "status" : { + "category" : "system.config.apply", + "level" : 300, + "message" : "success", + "timestamp" : "TIMESTAMP" + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_update.attr b/validator/sequences/scan_depth_trace/state_update.attr new file mode 100644 index 0000000000..4d554d6e6a --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_update.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "update", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_update.json b/validator/sequences/scan_depth_trace/state_update.json new file mode 100644 index 0000000000..6423f2909b --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_update.json @@ -0,0 +1,67 @@ +{ + "discovery" : { + "families" : { + "vendor" : { + "active_count" : 2, + "generation" : "TIMESTAMP", + "phase" : "stopped" + } + } + }, + "localnet" : { + "families" : { + "bacnet" : { + "addr" : "27312" + }, + "ether" : { + "addr" : "42:01:ac:11:23:ac" + }, + "ipv4" : { + "addr" : "172.17.35.172" + }, + "vendor" : { + "addr" : "28179023" + } + } + }, + "pointset" : { + "points" : { + "filter_alarm_pressure_status" : { + "units" : "No-units" + }, + "filter_differential_pressure_sensor" : { + "units" : "Degrees-Celsius" + }, + "filter_differential_pressure_setpoint" : { + "units" : "Bars", + "value_state" : "applied" + } + } + }, + "system" : { + "hardware" : { + "make" : "BOS", + "model" : "pubber" + }, + "last_config" : "TIMESTAMP", + "operation" : { + "last_start" : "TIMESTAMP", + "mode" : "initial", + "operational" : true, + "restart_count" : 1 + }, + "serial_no" : "sequencer-12734", + "software" : { + "firmware" : "v1", + "system" : "v1" + }, + "status" : { + "category" : "system.config.apply", + "level" : 300, + "message" : "success", + "timestamp" : "TIMESTAMP" + } + }, + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_validation.attr b/validator/sequences/scan_depth_trace/state_validation.attr new file mode 100644 index 0000000000..d52c1d2da2 --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_validation.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "validation", + "subType" : "state", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/state_validation.json b/validator/sequences/scan_depth_trace/state_validation.json new file mode 100644 index 0000000000..4930737629 --- /dev/null +++ b/validator/sequences/scan_depth_trace/state_validation.json @@ -0,0 +1,3 @@ +{ + "operation" : "REPLY" +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/upload_discovery.attr b/validator/sequences/scan_depth_trace/upload_discovery.attr new file mode 100644 index 0000000000..8de93cab82 --- /dev/null +++ b/validator/sequences/scan_depth_trace/upload_discovery.attr @@ -0,0 +1,10 @@ +{ + "deviceId" : "AHU-1", + "deviceNumId" : "123456789101112", + "deviceRegistryId" : "ZZ-TRI-FECTA", + "projectId" : "localhost", + "publishTime" : "TIMESTAMP", + "subFolder" : "discovery", + "subType" : "upload", + REDACTED_TXN_ID +} \ No newline at end of file diff --git a/validator/sequences/scan_depth_trace/upload_discovery.json b/validator/sequences/scan_depth_trace/upload_discovery.json new file mode 100644 index 0000000000..aa8abfd3a3 --- /dev/null +++ b/validator/sequences/scan_depth_trace/upload_discovery.json @@ -0,0 +1,8 @@ +{ + "event_no" : -3, + "family" : "vendor", + "generation" : "TIMESTAMP", + "sha256" : "f7550b4cc0b746098972fce79bf8dff6b6f96863fed070ac62004cf1f1e57e92", + "timestamp" : "TIMESTAMP", + "version" : "1.5.7" +} \ No newline at end of file diff --git a/validator/src/main/java/com/google/daq/mqtt/sequencer/SequenceBase.java b/validator/src/main/java/com/google/daq/mqtt/sequencer/SequenceBase.java index dd2be903f2..97e93ea150 100644 --- a/validator/src/main/java/com/google/daq/mqtt/sequencer/SequenceBase.java +++ b/validator/src/main/java/com/google/daq/mqtt/sequencer/SequenceBase.java @@ -154,6 +154,7 @@ import udmi.schema.CapabilityValidationState.CapabilityResult; import udmi.schema.Config; import udmi.schema.DiscoveryEvents; +import udmi.schema.DiscoveryUpload; import udmi.schema.Entry; import udmi.schema.Envelope; import udmi.schema.Envelope.SubFolder; @@ -215,7 +216,8 @@ public class SequenceBase { private static final Map, SubFolder> CLASS_EVENT_SUBTYPE_MAP = ImmutableMap.of( SystemEvents.class, SubFolder.SYSTEM, PointsetEvents.class, SubFolder.POINTSET, - DiscoveryEvents.class, SubFolder.DISCOVERY + DiscoveryEvents.class, SubFolder.DISCOVERY, + DiscoveryUpload.class, SubFolder.DISCOVERY ); private static final Map> EXPECTED_UPDATES = ImmutableMap.of( SubType.CONFIG, Config.class, @@ -550,6 +552,11 @@ protected static String messageCaptureBase(SubType subType, SubFolder subFolder) return format("%s_%s", useType, useFolder); } + private static String messageCaptureBase(Class clazz) { + SubType subType = clazz == DiscoveryUpload.class ? SubType.UPLOAD : SubType.EVENTS; + return messageCaptureBase(subType, CLASS_EVENT_SUBTYPE_MAP.get(clazz)); + } + private static void emitSequenceResult(SequenceResult result, String bucket, String name, String stage, int score, int total, String message) { // TODO: Clean up this hack of using a class-wide static variable to store this information. @@ -2071,9 +2078,11 @@ private void handleDeviceMessage(Envelope envelope, Map message, switch (envelope.subType) { // These are echos of sent partial config messages, so do nothing. case CONFIG -> trace("Ignoring echo configTransaction " + transactionId); + case DOWNLOAD -> trace("Ignoring echo downloadTransaction " + transactionId); // State updates are handled as a monolithic block with a state reflector update. case STATE -> trace("Ignoring partial state update"); case EVENTS -> handleEventMessage(envelope, message); + case UPLOAD -> captureMessage(envelope, message); default -> info("Encountered unexpected subType " + envelope.subType); } } @@ -2412,7 +2421,7 @@ private void writeSequencerLog(Entry logEntry) { * @return Number of messages */ protected int countReceivedEvents(Class clazz) { - String messageKey = messageCaptureBase(SubType.EVENTS, CLASS_EVENT_SUBTYPE_MAP.get(clazz)); + String messageKey = messageCaptureBase(clazz); List> events = getCapturedMessagesList(getDeviceId(), messageKey); if (events == null) { return 0; @@ -2421,7 +2430,7 @@ protected int countReceivedEvents(Class clazz) { } protected List popReceivedEvents(Class clazz) { - String messageKey = messageCaptureBase(SubType.EVENTS, CLASS_EVENT_SUBTYPE_MAP.get(clazz)); + String messageKey = messageCaptureBase(clazz); List> events = getCaptureMap(getDeviceId()).remove(messageKey); if (events == null) { return ImmutableList.of(); @@ -2430,6 +2439,10 @@ protected List popReceivedEvents(Class clazz) { .collect(Collectors.toList()); } + protected void provisionDownload(SubFolder subBlock, Object data) { + reflector().publish(getDeviceId(), format("%s/download", subBlock.value()), stringify(data)); + } + protected void withAlternateClient(Runnable evaluator) { withAlternateClient(false, evaluator); } diff --git a/validator/src/main/java/com/google/daq/mqtt/sequencer/sequences/BlobsetSequences.java b/validator/src/main/java/com/google/daq/mqtt/sequencer/sequences/BlobsetSequences.java index 323f207fca..34edd63573 100644 --- a/validator/src/main/java/com/google/daq/mqtt/sequencer/sequences/BlobsetSequences.java +++ b/validator/src/main/java/com/google/daq/mqtt/sequencer/sequences/BlobsetSequences.java @@ -12,6 +12,7 @@ import static com.google.udmi.util.JsonUtil.getNowInstant; import static com.google.udmi.util.JsonUtil.isoConvert; import static com.google.udmi.util.JsonUtil.stringify; +import static com.google.udmi.util.JsonUtil.stringifyTerse; import static java.lang.String.format; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -23,6 +24,7 @@ import static udmi.schema.Category.BLOBSET_BLOB_FETCH; import static udmi.schema.Category.BLOBSET_BLOB_PARSE; import static udmi.schema.Category.BLOBSET_BLOB_RECEIVE; +import static udmi.schema.FeatureDiscovery.FeatureStage.ALPHA; import static udmi.schema.FeatureDiscovery.FeatureStage.PREVIEW; import com.google.daq.mqtt.sequencer.DefaultLogLevel; @@ -49,6 +51,7 @@ import udmi.schema.BlobUpdateTestingModel; import udmi.schema.BlobsetConfig; import udmi.schema.BlobsetConfig.SystemBlobsets; +import udmi.schema.BlobsetDownload; import udmi.schema.EndpointConfiguration; import udmi.schema.EndpointConfiguration.Protocol; import udmi.schema.EndpointConfiguration.Transport; @@ -95,7 +98,8 @@ private static String generateEndpointConfigClientId(String registryId) { : format(IOT_CORE_CLIENT_ID_FMT, projectId, cloudRegion, registryId, getDeviceId()); } - private static String endpointConfigPayload(String hostname, String registryId) { + private static EndpointConfiguration makeEndpointConfiguration(String hostname, + String registryId) { EndpointConfiguration endpointConfiguration = new EndpointConfiguration(); endpointConfiguration.protocol = Protocol.MQTT; endpointConfiguration.hostname = hostname; @@ -110,7 +114,11 @@ private static String endpointConfigPayload(String hostname, String registryId) authProvider.basic.username = endpointConfiguration.client_id; authProvider.basic.password = siteModel.getDevicePassword(getDeviceId()); } - return stringify(endpointConfiguration); + return endpointConfiguration; + } + + private static String endpointConfigPayload(String hostname, String registryId) { + return stringify(makeEndpointConfiguration(hostname, registryId)); } private void untilClearedRedirect() { @@ -225,6 +233,33 @@ public void endpoint_connection_success_reconnect() { untilClearedRedirect(); } + @Feature(stage = ALPHA, bucket = ENDPOINT_CONFIG) + @Summary("Check endpoint redirection via download/blobset channel.") + @Test(timeout = TWO_MINUTES_MS) + public void endpoint_connection_download() { + EndpointConfiguration endpointConfig = + makeEndpointConfiguration(getAlternateEndpointHostname(), registryId); + BlobsetDownload download = new BlobsetDownload(); + download.blobs = new HashMap<>(); + download.blobs.put(IOT_BLOB_KEY, endpointConfig); + provisionDownload(SubFolder.BLOBSET, download); + + BlobBlobsetConfig config = new BlobBlobsetConfig(); + config.url = SemanticValue.describe("endpoint download url", "download://" + IOT_BLOB_KEY); + config.phase = BlobPhase.FINAL; + config.generation = SemanticDate.describe("blob generation", new Date()); + config.sha256 = SemanticValue.describe("blob data hash", + sha256(stringifyTerse(endpointConfig))); + BlobsetConfig blobset = new BlobsetConfig(); + blobset.blobs = new HashMap<>(); + blobset.blobs.put(IOT_BLOB_KEY, config); + deviceConfig.blobset = blobset; + debug("blobset download config", stringify(deviceConfig.blobset)); + + untilSuccessfulRedirect(BlobPhase.FINAL); + untilClearedRedirect(); + } + @Feature(stage = PREVIEW, bucket = ENDPOINT_CONFIG) @Summary("Failed connection because of bad hash.") @ValidateSchema(SubFolder.BLOBSET) diff --git a/validator/src/main/java/com/google/daq/mqtt/sequencer/sequences/DiscoverySequences.java b/validator/src/main/java/com/google/daq/mqtt/sequencer/sequences/DiscoverySequences.java index bbb981a1d9..b852aebcd8 100644 --- a/validator/src/main/java/com/google/daq/mqtt/sequencer/sequences/DiscoverySequences.java +++ b/validator/src/main/java/com/google/daq/mqtt/sequencer/sequences/DiscoverySequences.java @@ -18,6 +18,7 @@ import static com.google.udmi.util.GeneralUtils.ifNullElse; import static com.google.udmi.util.GeneralUtils.ifTrueThen; import static com.google.udmi.util.GeneralUtils.joinOrNull; +import static com.google.udmi.util.GeneralUtils.sha256; import static com.google.udmi.util.JsonUtil.getNowInstant; import static com.google.udmi.util.JsonUtil.isoConvert; import static com.google.udmi.util.JsonUtil.stringifyTerse; @@ -54,9 +55,11 @@ import com.google.daq.mqtt.sequencer.semantic.SemanticDate; import com.google.daq.mqtt.sequencer.semantic.SemanticList; import com.google.daq.mqtt.util.providers.FamilyProvider; +import java.io.ByteArrayOutputStream; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; +import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -75,6 +78,7 @@ import udmi.schema.Bucket; import udmi.schema.DiscoveryConfig; import udmi.schema.DiscoveryEvents; +import udmi.schema.DiscoveryUpload; import udmi.schema.Enumerations; import udmi.schema.Enumerations.Depth; import udmi.schema.Envelope.SubFolder; @@ -734,6 +738,75 @@ public void scan_periodic_now_enumerate() { ifNotEmptyThrow(checkEnumeration(lastGenerationEvents, true), CSV_JOINER::join); } + @Test(timeout = TWO_MINUTES_MS) + @Feature(bucket = DISCOVERY_SCAN, stage = ALPHA, facets = SubFolder.DISCOVERY) + @Summary("Check results of a single scan with trace depth") + public void scan_depth_trace() { + initializeDiscovery(); + checkState(scanGeneration == null, "scanStartTime not null"); + Date scanStart = cleanInstantDate(Instant.now().minusSeconds(1)); + scanGeneration = scanStart.toInstant(); + popReceivedEvents(DiscoveryUpload.class); + configureScan(scanGeneration, null, DEFAULT_ENUMERATION, Depth.TRACE, null, null); + final Instant scanConfigured = getNowInstant(); + + waitUntil("scheduled scan active", WAITING_PERIOD, this::detailScanActive); + final Instant scanStarted = getNowInstant(); + long deltaStart = Math.abs(Duration.between(scanStarted, scanConfigured).toSeconds()); + checkThat("scan started at time", deltaStart <= SCAN_START_JITTER_SEC, + format("scan start %s %ss different from expected %s", isoConvert(scanStarted), deltaStart, + isoConvert(scanConfigured))); + + final Instant expectedFinish = scanStarted.plusSeconds(SCAN_DURATION_SEC); + waitUntil("scheduled scan complete", WAITING_PERIOD, this::detailScanStopped); + Instant scanFinished = getNowInstant(); + long deltaFinish = Math.abs(Duration.between(scanFinished, expectedFinish).toSeconds()); + checkThat("scan completed at time", deltaFinish <= SCAN_START_JITTER_SEC, + format("scan completed %ss different from expected %s", deltaFinish, + isoConvert(expectedFinish))); + + final int actualCount = deviceState.discovery.families.get(scanFamily).active_count; + checkThat("trace scan reported positive active_count", actualCount > 0); + sleepFor("discovery uploads synchronized", EVENT_JITTER_SLEEP_TIME); + + List events = popReceivedEvents(DiscoveryEvents.class); + checkThat("no discovery events emitted during trace scan", events.isEmpty()); + + List uploads = popReceivedEvents(DiscoveryUpload.class); + final int expectedUploads = actualCount + EVENT_MARKERS; + checkThat("received expected number of discovery uploads", + uploads.size() == expectedUploads, + format("expected %d uploads, received %d", expectedUploads, uploads.size())); + checkThat("all discovery uploads match scan family and generation", + uploads.stream().allMatch(u -> scanFamily.equals(u.family) + && dateEquals(u.generation, Date.from(scanGeneration)))); + + SortedSet eventNos = uploads.stream().map(u -> u.event_no) + .filter(Objects::nonNull).collect(Collectors.toCollection(TreeSet::new)); + checkThat("received all unique upload event numbers", eventNos.size() == expectedUploads); + final Integer endEventNo = eventNos.removeFirst(); + checkThat("received proper upload start marker", eventNos.getFirst() == 0); + checkThat("received proper last upload chunk", eventNos.getLast() == actualCount); + checkThat("received proper upload termination marker", endEventNo == -(actualCount + 1)); + + Map uploadsByEventNo = uploads.stream() + .collect(Collectors.toMap(u -> u.event_no, Function.identity())); + ByteArrayOutputStream assembled = new ByteArrayOutputStream(); + for (int chunkNo = 1; chunkNo <= actualCount; chunkNo++) { + DiscoveryUpload chunk = uploadsByEventNo.get(chunkNo); + checkThat(format("upload chunk %d data present", chunkNo), + chunk != null && chunk.data != null); + assembled.writeBytes(Base64.getDecoder().decode(chunk.data)); + } + + DiscoveryUpload eofUpload = uploadsByEventNo.get(endEventNo); + checkThat("upload termination marker includes sha256", eofUpload.sha256 != null); + String actualSha256 = sha256(assembled.toByteArray()); + checkThat("assembled upload matches termination marker sha256", + eofUpload.sha256.equalsIgnoreCase(actualSha256), + format("expected %s, got %s", eofUpload.sha256, actualSha256)); + } + private void initializeDiscovery() { initializeFamilies(); deviceConfig.discovery = new DiscoveryConfig(); diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java b/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java index f091551df5..08eddbbb29 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java @@ -154,12 +154,14 @@ public class Validator { private static final String CONFIG_CATEGORY = "config"; private static final Set INTERESTING_TYPES = ImmutableSet.of( SubType.EVENTS.value(), - SubType.STATE.value()); + SubType.STATE.value(), + SubType.UPLOAD.value()); private static final Set IGNORE_FOLDERS = ImmutableSet.of( SubFolder.ERROR.value()); private static final Set LAST_SEEN_SUBTYPES = ImmutableSet.of( SubType.EVENTS, - SubType.STATE); + SubType.STATE, + SubType.UPLOAD); private static final long DEFAULT_INTERVAL_SEC = 60; private static final long REPORTS_PER_SEC = 10;