{"id":2992,"date":"2021-08-30T03:14:17","date_gmt":"2021-08-30T10:14:17","guid":{"rendered":"http:\/\/www.taosdata.com.cn:88\/blog\/?p=2992"},"modified":"2025-03-30T19:58:36","modified_gmt":"2025-03-31T02:58:36","slug":"the-application-of-tdengine-in-the-heygears-3d-printing-system","status":"publish","type":"post","link":"https:\/\/tdengine.com\/the-application-of-tdengine-in-the-heygears-3d-printing-system\/","title":{"rendered":"Simplified, Efficient Monitoring and Storage of Industrial Equipment Metrics"},"content":{"rendered":"\n<h2 class=\"gb-headline gb-headline-0254efa6 gb-headline-text\">Introduction<\/h2>\n\n\n\n<p>For an&nbsp;industrial IoT&nbsp;or IoT system, the most basic requirement&nbsp;is&nbsp;to&nbsp;fit curves to a series of data points&nbsp;(devices). Take the power curve as an example, alike stock price charts,&nbsp;it&nbsp;will take the last power value reported by the device in each minute as the power of&nbsp;the device in&nbsp;this minute.&nbsp;If the device does not report the power value&nbsp;in a certain minute, the power value reported at the last&nbsp;minute will be counted. Examples are as follows.<\/p>\n\n\n\n<figure class=\"gb-block-image gb-block-image-afc3de06\"><img decoding=\"async\" width=\"662\" height=\"792\" class=\"gb-image gb-image-afc3de06\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-01-example-1.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-01-example-1.png?strip=all&amp;sharp=1 662w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-01-example-1-251x300.png?strip=all&amp;sharp=1 251w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-01-example-1.png?strip=all&amp;sharp=1&amp;w=132 132w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-01-example-1.png?strip=all&amp;sharp=1&amp;w=397 397w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-01-example-1.png?strip=all&amp;sharp=1&amp;w=529 529w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-01-example-1.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 662px) 100vw, 662px\" \/><\/figure>\n\n\n\n<p>Obtaining the curve&nbsp;like:<\/p>\n\n\n\n<figure class=\"gb-block-image gb-block-image-33052fe2\"><img decoding=\"async\" width=\"664\" height=\"734\" class=\"gb-image gb-image-33052fe2\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-02-example-2.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-02-example-2.png?strip=all&amp;sharp=1 664w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-02-example-2-271x300.png?strip=all&amp;sharp=1 271w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-02-example-2.png?strip=all&amp;sharp=1&amp;w=132 132w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-02-example-2.png?strip=all&amp;sharp=1&amp;w=398 398w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-02-example-2.png?strip=all&amp;sharp=1&amp;w=531 531w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-02-example-2.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 664px) 100vw, 664px\" \/><\/figure>\n\n\n\n<figure class=\"gb-block-image gb-block-image-b81167f8\"><img decoding=\"async\" width=\"1557\" height=\"442\" class=\"gb-image gb-image-b81167f8\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3.png?strip=all&amp;sharp=1 1557w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3-300x85.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3-1024x291.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3-768x218.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3-1536x436.png?strip=all&amp;sharp=1 1536w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3.png?strip=all&amp;sharp=1&amp;w=622 622w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3.png?strip=all&amp;sharp=1&amp;w=934 934w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3.png?strip=all&amp;sharp=1&amp;w=1245 1245w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-03-example-3.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n\n\n\n<h2 class=\"gb-headline gb-headline-767937c1 gb-headline-text\">Pain Points in the Usage Scenarios<\/h2>\n\n\n\n<p>Typically,&nbsp;we will write the data reported into Apache Kafka first. If it is an offline computing scenario, we will&nbsp;write&nbsp;data into Hive&nbsp;and then use&nbsp;Spark SQL to read&nbsp;data at a regular interval, afterwards,&nbsp;the&nbsp;results will be written&nbsp;into HBase. When it comes to a real-time computing scenario,&nbsp;we will employ&nbsp;Apache Flink to consume the data&nbsp;from Kafka&nbsp;and write the results into HBase, while in this case we&nbsp;also need to deal with issues like&nbsp;irregular data&nbsp;and delayed messages.<\/p>\n\n\n\n<p>Moreover, based&nbsp;on&nbsp;Hadoop, we have to integrate&nbsp;ZooKeeper, HDFS, Hive,&nbsp;and HBase&nbsp;to implement our requirements, which leads to high O&amp;M costs. In addition, HBase relies on the key-value (KV) to store&nbsp;time-series&nbsp;data, which will occupy storage&nbsp;space to store repetitive&nbsp;KVs.<\/p>\n\n\n\n<p>Except for the pain points mentioned above, we&nbsp;also need to consider&nbsp;requirements such as&nbsp;data growth, data reconciliation&nbsp;(DR), and disaster recovery.<\/p>\n\n\n\n<p>HeyGears&nbsp;provides&nbsp;customers with full-stack&nbsp;solutions based on 3D printing technology, hence we need to&nbsp;continuously&nbsp;monitor&nbsp;the operating status of the equipment and store large volumes of metric&nbsp;data. Therefore, we have conducted research on prevailing <a href=\"https:\/\/tdengine.com\/what-is-a-time-series-database\/\">time-series databases<\/a> (TSDB) and selected&nbsp;TDengine&nbsp;as a solution.<\/p>\n\n\n\n<p>According to our research, TDengine is an open-sourced IoT big data platform capable of handling large data volumes with industry-leading high performance and high storage efficiency. It has been well received by the developer community since it was made available under an open-source license two years ago, garnering nearly 16,000 Github stars so far and hundreds of clones on a daily basis.<\/p>\n\n\n\n<p>Referring to the documentation of TDengine, the problems we\u2019ve encountered&nbsp;can be easily solved with&nbsp;its simple&nbsp;SQL-like syntax.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">select last(val) a from super_table_xx where ts &gt;= &#039;2021-06-07 18:10:00&#039; and ts &lt;= &#039;2021-06-07 18:20:00&#039; interval(60s) fill(value, 0);<\/code><\/pre>\n\n\n\n<h2 class=\"gb-headline gb-headline-34fc2daa gb-headline-text\">Remarkable STable and Table Design<\/h2>\n\n\n\n<p>Why does TDengine present such a powerful efficiency? It lies in the unique design of STable and table. On the one hand, TDengine uses &#8220;one table for one data collection point (device)&#8221; to fit in the typical IoT scenario where the data is generated by the single device and timestamped at a regular time interval. In the actual usage scenarios, no matter real-time queries or offline data analysis, reading data from a single device is always filtered by a consistent time interval.<\/p>\n\n\n\n<p>On the other hand, to achieve higher management efficiency, TDengine development team invented STable to deal with the same type of data points (devices).<\/p>\n\n\n\n<p>For instance, if we want to store the temperature data and humidity data of the equipment, STable can be created in this way:<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">create stable if not exists s_device (ts TIMESTAMP,\n  temperature double,\n  humidity double\n) TAGS (device_sn BINARY(1000));<\/code><\/pre>\n\n\n\n<p>In actual use, for example, the SQL&nbsp;statement&nbsp;to perform \u2018INSERT data\u2019&nbsp;for devices &#8216;d1&#8217; and &#8216;d2&#8217; is as follows.<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">insert into s_device_d1 (ts, temperature, humidity) USING s_device (device_sn) TAGS (&#039;d1&#039;) values (1623157875000, 35.34, 80.24);\ninsert into s_device_d2 (ts, temperature, humidity) USING s_device (device_sn) TAGS (&#039;d2&#039;) values (1623157891000, 29.63, 79.48);<\/code><\/pre>\n\n\n\n<p>The query&nbsp;for&nbsp;data from device \u2018d1\u2019&nbsp;in&nbsp;a certain time interval&nbsp;is like this:<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">select * from s_device where device_sn = &#039;d1&#039; and ts &gt; 1623157871000 and ts &lt; 1623157890000 ;<\/code><\/pre>\n\n\n\n<p>If selecting&nbsp;the average temperature data&nbsp;for the last 7 days&nbsp;by 1-hour interval, the statement can be:<\/p>\n\n\n\n<pre class=\"wp-block-code language-sql\"><code class=\"\" data-line=\"\">select avg(temperature) temperature from s_device where  device_sn = #{deviceSn} and ts &gt;= #{startTime} and ts &lt; #{endTime}  interval(1h)<\/code><\/pre>\n\n\n\n<p>TDengine also provides many aggregation functions. Except \u2018last\u2019&nbsp;and \u2018fill\u2019&nbsp;used in the cases above, other commonly used functions such as \u2018sum\u2019&nbsp;and \u2018max\u2019&nbsp;are also included.<\/p>\n\n\n\n<p>In the process of integrating with applications, we use MyBatis, a flexible and easy-to-use ORM framework.&nbsp;For&nbsp;example, for the table &#8216;s_device&#8217; above, we defined&nbsp;the entity&nbsp;at first:<\/p>\n\n\n\n<pre class=\"wp-block-code language-java\"><code class=\"\" data-line=\"\">import com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.sql.Timestamp;\n\n\/**\n * @author: DaLuo\n * @date: 2021\/06\/25\n * @description:\n *\/\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@Builder\n@TableName(value = &quot;s_device&quot;)\npublic class TestSuperDeviceEntity {\n\n    private Timestamp ts;\n    private Float temperature;\n    private Float humidity;\n    @TableField(value = &quot;device_sn&quot;)\n    private String device_sn ;\n}<\/code><\/pre>\n\n\n\n<p>And then we defined&nbsp;the mapper:<\/p>\n\n\n\n<pre class=\"wp-block-code language-java\"><code class=\"\" data-line=\"\">import com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.hg.device.kafka.tdengine.entity.TestSuperDeviceEntity;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.sql.Timestamp;\nimport java.util.List;\n\n\/**\n * @author: DaLuo\n * @date: 2021\/06\/25\n * @description:\n *\/\n@Mapper\npublic interface TestSuperDeviceMapper extends BaseMapper&lt;TestSuperDeviceEntity&gt; {\n\n    \/**\n     * insert\n     * @param entity\n     * @return\n     *\/\n    @Insert({\n            &quot;INSERT INTO &#039;s_device_${entity.deviceSn}&#039; (ts ,temperature, humidity ) &quot;,\n            &quot;USING s_device (device_sn) TAGS (#{entity.deviceSn}) &quot;,\n            &quot;VALUES (#{entity.ts}, #{entity.temperature}, #{entity.humidity})&quot;\n    })\n    int insertOne(@Param(value = &quot;entity&quot;) TestSuperDeviceEntity entity);\n\n\n    \/**\n     * batch writing\n     * @param entities\n     * @return\n     *\/\n    @Insert({\n            &quot;&lt;script&gt;&quot;,\n            &quot;INSERT INTO &quot;,\n            &quot;&lt;foreach collection=&#039;list&#039; item=&#039;item&#039; separator=&#039; &#039;&gt;&quot;,\n            &quot;&#039;s_device_${item.deviceSn}&#039; (ts ,temperature, humidity) USING s_device (device_sn) TAGS (#{item.deviceSn}) &quot;,\n            &quot;VALUES (#{item.ts}, #{item.temperature}, #{item.humidity})&quot;,\n            &quot;&lt;\/foreach&gt;&quot;,\n            &quot;&lt;\/script&gt;&quot;\n    })\n    int batchInsert(@Param(&quot;list&quot;) List&lt;TestSuperDeviceEntity&gt; entities);\n\n    \/**\n     *  select the average temperature data&nbsp;for the last 7 days&nbsp;by 1-hour interval\n     * @param deviceSn\n     * @param startTime inclusive\n     * @param endTime   exclusive\n     * @return\n     *\/\n    @Select(&quot;select avg(temperature) temperature from s_device where  device_sn = #{deviceSn} and ts &gt;= #{startTime} and ts &lt; #{endTime}  interval(1h)&quot;)\n    List&lt;TempSevenDaysTemperature&gt; selectSevenDaysTemperature(\n            @Param(value = &quot;deviceSn&quot;) String deviceSn,\n            @Param(value = &quot;startTime&quot;) long startTime,\n            @Param(value = &quot;endTime&quot;) long endTime);\n\n    @AllArgsConstructor\n    @NoArgsConstructor\n    @Data\n    @Builder\n    class TempSevenDaysTemperature {\n        private Timestamp ts;\n        private float temperature;\n    }\n}<\/code><\/pre>\n\n\n\n<p>Another amazing feature of TDengine is no need for creating tables in advance. By setting&nbsp;the &#8216;tag&#8217; as a part of \u2018table_name\u2019, it will automatically create tables when inserting data.<\/p>\n\n\n\n<h2 class=\"gb-headline gb-headline-f1e2ce62 gb-headline-text\">Connectors<\/h2>\n\n\n\n<p>We can connect to TDengine via TDengine JDBC-driver in two ways &#8212; JDBC-JNI and JDBC-RESTful. JDBC-JNI demonstrates higher ingestion performance while it requires to install TDengine client on the server where the application is running.<\/p>\n\n\n\n<p>We have&nbsp;deployed our application in the&nbsp;Kubernetes cluster, while&nbsp;the program is running in&nbsp;Docker.&nbsp;Therefore, we created an image&nbsp;of the application program. Here is the&nbsp;Dockerfile of the base image:<\/p>\n\n\n\n<pre class=\"wp-block-code language-dockerfile\"><code class=\"\" data-line=\"\">FROM openjdk:8-jdk-oraclelinux7\n&nbsp;\nCOPY TDengine-client-2.0.16.0-Linux-x64.tar.gz \/\n&nbsp;\nRUN tar -xzvf \/TDengine-client-2.0.16.0-Linux-x64.tar.gz &amp;&amp;&nbsp; cd \/TDengine-client-2.0.16.0&nbsp;&amp;&amp;&nbsp; pwd &amp;&amp; ls &amp;&amp; .\/install_client.sh<\/code><\/pre>\n\n\n\n<p>build\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code language-shell\"><code class=\"\" data-line=\"\">docker build -t tdengine-openjdk-8-runtime:2.0.16.0 -f Dockerfile .<\/code><\/pre>\n\n\n\n<p>The Dockerfile shows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-dockerfile\"><code class=\"\" data-line=\"\">FROM tdengine-openjdk-8-runtime:2.0.16.0\n&nbsp;\nENV JAVA_OPTS=&quot;-Duser.timezone=Asia\/Shanghai -Djava.security.egd=file:\/dev\/.\/urandom&quot;\n&nbsp;\nCOPY app.jar \/app.jar\n&nbsp;\nENTRYPOINT &#091;&quot;java&quot;,&quot;-jar&quot;,&quot;\/app.jar&quot;]<\/code><\/pre>\n\n\n\n<p>Thus, the application can&nbsp;be scheduled on any node&nbsp;in Kubernetes.<\/p>\n\n\n\n<p>In addition, our program involves automated scheduling, which requires frequent&nbsp;message transports between upper machines and lower machines via MQTT. For example, the cloud sends&nbsp;command&nbsp;1000-&#8220;start task A&#8221;, and&nbsp;then&nbsp;the lower machine responds a command 2000-&#8220;received task A&#8221;. If regarding the command as a device and the sequence&nbsp;number of the command&nbsp;as its attributes, this kind of data will also be compatible&nbsp;to be stored in TDengine.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">*************************** 1.row ***************************\n       ts: 2021-06-23 16:10:30.000\n      msg: {\"task_id\":\"7b40ed4edc1149f1837179c77d8c3c1f\",\"action\":\"start\"}\ndevice_sn: deviceA\n     kind: 1000\n*************************** 2.row ***************************\n       ts: 2021-06-23 16:10:31.000\n      msg: {\"task_id\":\"7b40ed4edc1149f1837179c77d8c3c1f\",\"action\":\"received\"}\ndevice_sn: deviceA\n     kind: 2000<\/pre>\n\n\n\n<h2 class=\"gb-headline gb-headline-30629c74 gb-headline-text\">Conclusion<\/h2>\n\n\n\n<p>In the end, TDengine also has a STable \u2018log.dn\u2019&nbsp;which&nbsp;stores metric data including memory usage, CPU usage, etc., hence we can utilize&nbsp;Grafana to visualize those metrics as a reliable reference for monitoring.<\/p>\n\n\n\n<figure class=\"gb-block-image gb-block-image-21ee6bc7\"><img decoding=\"async\" width=\"1568\" height=\"750\" class=\"gb-image gb-image-21ee6bc7\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard.png?strip=all&sharp=1\" alt=\"\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard.png?strip=all&amp;sharp=1 1568w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard-300x143.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard-1024x490.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard-768x367.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard-1536x735.png?strip=all&amp;sharp=1 1536w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard.png?strip=all&amp;sharp=1&amp;w=627 627w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard.png?strip=all&amp;sharp=1&amp;w=940 940w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard.png?strip=all&amp;sharp=1&amp;w=1254 1254w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/22.013-04-dashboard.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>HeyGears selected TDengine to handle time-series data storage and processing for its 3D printing devices.<\/p>\n","protected":false},"author":81,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"content-type":"","footnotes":""},"categories":[23,268,231],"tags":[],"ppma_author":[167],"class_list":["post-2992","post","type-post","status-publish","format-standard","hentry","category-case-studies","category-industrial-data","category-manufacturing"],"authors":[{"term_id":167,"user_id":81,"is_guest":0,"slug":"chait","display_name":"Chait Diwadkar","avatar_url":{"url":"https:\/\/tdengine.com\/wp-content\/uploads\/29.03-05-cdiwadkar.jpg","url2x":"https:\/\/tdengine.com\/wp-content\/uploads\/29.03-05-cdiwadkar.jpg"},"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/2992","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/users\/81"}],"replies":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/comments?post=2992"}],"version-history":[{"count":10,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/2992\/revisions"}],"predecessor-version":[{"id":24611,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/2992\/revisions\/24611"}],"wp:attachment":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/media?parent=2992"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/categories?post=2992"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/tags?post=2992"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/ppma_author?post=2992"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}