{"id":8443,"date":"2018-01-27T09:37:12","date_gmt":"2018-01-27T09:37:12","guid":{"rendered":"http:\/\/localhost:8105\/?p=8443"},"modified":"2021-12-09T07:29:19","modified_gmt":"2021-12-09T07:29:19","slug":"x-plane-plugins-some-notes","status":"publish","type":"post","link":"https:\/\/blog.shahada.abubakar.net\/?p=8443","title":{"rendered":"X-Plane Plugins &#8211; Some Notes"},"content":{"rendered":"<div><\/div>\n<div>I&#8217;ve been stumped for the last few weeks with getting my Raspberry Pi FMC to work with Zibo&#8217;s 737. I used the ExtPlane plugin to interface my code on the Pi with the simulator, and it works well for almost all FMCs except Zibo&#8217;s. The symptoms would be missing or truncated data updates, causing the screen on the Pi to get messed up.<\/div>\n<div><\/div>\n<div>Since I&#8217;m only working on the Pi end, it&#8217;s hard to tell whether the problem is with ExtPlane or Zibo. The AirFMC iPad app, which comes with its own plugin, is able to replicate the Zibo screen without any issues, so I thought I should try to write a plugin to access the Zibo datarefs and see what that looks like. If so, then I could probably extend that data to the network and have my Pi code get to the data.<\/div>\n<div><\/div>\n<div>So the first stop is to download the <a href=\"https:\/\/developer.x-plane.com\/code-sample\/hello-world-sdk-3\/\">X-Plane SDK&#8217;s Hello World<\/a> plugin. This comes in three flavours: an XCode one for Mac OSX, a Visual Studio 2010 version for windows, and a GCC Linux version. I grabbed the Linux version, as that&#8217;s what I run on my development PC (I also have an X-Plane install here for testing stuff like this). The Linux version is the easiest to build as you just have to run make (the SDK is embedded in the example). It compiled the example C program and linked it against the SDK, and built a shared library under Hello-World-SDK-3 called lin.xpl. I symlinked the Hello-World-SDK-3 folder into X-Plane&#8217;s Resources\/plugins folder, and started up X-Plane, and got this:<\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-9076\" src=\"https:\/\/blog.shahada.abubakar.net\/wp-content\/uploads\/2018\/01\/Screenshot-from-2018-01-27-18-34-08.png\" alt=\"\" width=\"1280\" height=\"748\" srcset=\"https:\/\/blog.shahada.abubakar.net\/wp-content\/uploads\/2018\/01\/Screenshot-from-2018-01-27-18-34-08.png 1280w, https:\/\/blog.shahada.abubakar.net\/wp-content\/uploads\/2018\/01\/Screenshot-from-2018-01-27-18-34-08-300x175.png 300w, https:\/\/blog.shahada.abubakar.net\/wp-content\/uploads\/2018\/01\/Screenshot-from-2018-01-27-18-34-08-1024x598.png 1024w, https:\/\/blog.shahada.abubakar.net\/wp-content\/uploads\/2018\/01\/Screenshot-from-2018-01-27-18-34-08-768x449.png 768w\" sizes=\"auto, (max-width: 1280px) 100vw, 1280px\" \/><\/div>\n<div><\/div>\n<div><\/div>\n<div><b><span style=\"font-size: 18px;\"><span style=\"font-family: gotham, helvetica, arial, sans-serif;\">Analyzing Hello-World-SDK-3.cpp<\/span><\/span><\/b><\/div>\n<div><\/div>\n<div>Let&#8217;s take a look at the Hello-World-SDK-3.cpp &#8230;<\/div>\n<div><\/div>\n<div>Each plugin is <a href=\"https:\/\/developer.x-plane.com\/?article=developing-plugins\">required<\/a> to declared the following five C functions:<\/div>\n<div><\/div>\n<ul>\n<li><b>int XPluginStart (char * outName, char * outSig, char * outDesc)<\/b>\n<ul>\n<li>This is called when the plugin is started. For a global plugin (one which runs regardless of what aircraft or scenery is being used, and is found in the Resources\/plugins folder), this is usually called while X-Plane itself is starting up.<\/li>\n<li>The minimal the plugin must do is copy the plugin name, a unique plugin identifier, and plugin description into the char buffer pointers provider (up to 256 characters).<\/li>\n<li>The plugin can then do any other stuff it wants to do during startup. In the case of Hello-World-SDK-3, it uses the API&#8217;s from XPMLDisplay.h and XPMLGRaphics.h to create a window withing X-PLane.<\/li>\n<\/ul>\n<\/li>\n<li><b>void XPluginStop (void)<\/b>\n<ul>\n<li>This is called when the plugin is stopped.<\/li>\n<li>In the Hello-World-SDK-3 example, the window created is destroyed.<\/li>\n<\/ul>\n<\/li>\n<li><b>int XPluginEnable (void)<\/b>\n<ul>\n<li>This doesn&#8217;t do anything in Hello-World-SDK3.<\/li>\n<\/ul>\n<\/li>\n<li><b>void XPluginDisable (void)<\/b>\n<ul>\n<li>This doesn&#8217;t do anything in Hello-World-SDK3. It just returns 1.<\/li>\n<\/ul>\n<\/li>\n<li><b>void XPluginReceiveMessage (XPLMPluginID inFrom, int inMsg, void * inParam)<\/b>\n<ul>\n<li><span style=\"font-family: gotham, helvetica, arial, sans-serif; font-size: 14px;\">This doesn&#8217;t do anything in Hello-World-SDK3.<\/span><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<div><\/div>\n<div>On it&#8217;s own, other than declaring stuff, the plugin doesn&#8217;t get to do anything much, unless it registers callbacks to itself. It is in the callback that the plugin gets to &#8220;do its thing&#8221;. For example, in the case of Hello-World-SDK3, when the window is created, a list of callbacks are specified which trigger when the following happen:<\/div>\n<div><\/div>\n<ul>\n<li>the window needs to be drawn<\/li>\n<li>the user clicks in the window<\/li>\n<li>the user right clicks in the widow<\/li>\n<li>a key is pressed in the window<\/li>\n<li>the cursor is moved in the wondow<\/li>\n<\/ul>\n<div><\/div>\n<div>Most of these are set to a dummy handler, except for the draw window function, in which Hello-World-SDK-3 writes &#8220;Hello World&#8221; within the window.<\/div>\n<div><\/div>\n<div>There are <a href=\"https:\/\/developer.x-plane.com\/?article=developing-plugins#Registering_Additional_Callbacks\">many other ways<\/a> that a plugin can register a callback, depending on what is needed. A common callback used for plugins that need to do stuff regularly is to use the <a href=\"https:\/\/developer.x-plane.com\/sdk\/XPLMProcessing\/\">XPLMProcessing API<\/a>, where callbacks can be tied into X-Plane&#8217;s flight loop.<\/div>\n<div><\/div>\n<div><\/div>\n<div><b><span style=\"font-size: 18px;\">Adding &#8220;GOT HERE&#8221;<\/span><\/b><\/div>\n<div><b>\u00a0<\/b><\/div>\n<div>The next thing i did was add <span style=\"font-family: 'courier new', courier, monospace;\"><a href=\"https:\/\/blog.shahada.abubakar.net\/?tag=include\">#include<\/a> &lt;stdio.h&gt;<\/span> at the top of Hello-World-SDK3, and sprinked some printfs around the existing functions and callbacks. I then recompiled the plugin and launched X-Plane from a terminal. This gave me some confidence that I could trace the plugin and get output, and also some idea of when each callback is called.<\/div>\n<div><\/div>\n<div>Interestingly, the drawWindow function got called, even when the simulator was still at the main menu.<\/div>\n<div><\/div>\n<div>So what&#8217;s the difference between Start and Disable? Start is more for declaring resources your plugin provides, and enable is more for making use of resources that other plugins or the sim provides. This way, there is less chance of requiring a resource that hasn&#8217;t been declared yet.<\/div>\n<div><\/div>\n<div>I only got XPluginDisable() and XPluginStop() calls when X-Plane was shutdown. Incidentally, I saw two calls to XPluginReceiveMessage() &#8230; so this is probably something that should be looked at.<\/div>\n<div><\/div>\n<div><\/div>\n<div><span style=\"font-size: 18px;\"><b>Adding a Processing Callback<\/b><\/span><\/div>\n<div><b>\u00a0<\/b><\/div>\n<div>Let&#8217;s modify Hello-World-SDK3 to have a Processing callback. First we need to include the appropriate headers with a<\/div>\n<div><span style=\"font-family: 'courier new', courier, monospace;\">\u00a0<\/span><\/div>\n<div><span style=\"font-family: 'courier new', courier, monospace;\"><a href=\"https:\/\/blog.shahada.abubakar.net\/?tag=include\">#include<\/a> &#8220;XPLMProcessing.h&#8221;<\/span>.<\/div>\n<div><\/div>\n<div>Then I added a function that will be called every 1 second:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">PLUGIN_API float flight_loop_callback (<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  float inElapsedSinceLastCall,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  float inElapsedTimeSinceLastFlightLoop,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  int inCounter,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  void * inRefcon) {<\/span>\n\n<span style=\"font-family: 'courier new', courier, monospace;\">  printf (\"SHA: in flight_loop_callback\\n\");<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  return 1.0;<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">};<\/span><\/pre>\n<div><\/div>\n<div>This is then registered inside the XPluginEnable() function with:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">\/\/ register a FlightLoopCallback<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\"> XPLMRegisterFlightLoopCallback (<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  &amp;flight_loop_callback,<\/span><span style=\"font-family: 'courier new', courier, monospace;\">  1,<\/span><span style=\"font-family: 'courier new', courier, monospace;\">\n NULL<\/span><span style=\"font-family: 'courier new', courier, monospace;\">\n);<\/span><\/pre>\n<div><\/div>\n<div>And deregistered inside XPLuginDisable() function with:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">XPLMUnregisterFlightLoopCallback (<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  &amp;flight_loop_callback,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  NULL<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">);<\/span><\/pre>\n<div><\/div>\n<div>Now when X-Plane is run I get this output every 1 second:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace; font-size: 14px;\">SHA: in flight_loop_callback<\/span>\n<span style=\"font-family: 'courier new', courier, monospace; font-size: 14px;\">SHA: in flight_loop_callback<\/span>\n<span style=\"font-family: 'courier new', courier, monospace; font-size: 14px;\">SHA: in flight_loop_callback<\/span><\/pre>\n<div><\/div>\n<div>Note that this only starts when the sim is running proper i.e. it is not called when at the X-Plane menu.\u00c2<\/div>\n<div><\/div>\n<div><\/div>\n<div><span style=\"font-size: 18px;\"><b>Fetching a Dataref<\/b><\/span><\/div>\n<div><\/div>\n<div>Next, let&#8217;s see if we can fetch a dataref inside our flight_loop_callback.<\/div>\n<div><\/div>\n<div>It seems that you have to poll regularly to get the value of a dataref &#8230; there is no way to do a callback when a dataref changes value (Unless it is a &#8220;shared&#8221; dataref. I&#8221;m not sure if Zibo&#8217;s datarefs are shared or not, so lets assume it&#8217;s owned by the zibo plugin).<\/div>\n<div><\/div>\n<div>Also to access datarefs, you have to call XPLMFindDataRef with the dataref string. This then returns a XPLMDataRef, which can then be interrogated at any time to get the value of the dataref.<\/div>\n<div><\/div>\n<div>So coming back to our hacked-up Hello-World-SDK-3 code, we add in the header for data access:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\"><a href=\"https:\/\/blog.shahada.abubakar.net\/?tag=include\">#include<\/a> \"XPLMDataAccess.h\"<\/span><\/pre>\n<div><\/div>\n<div>We also declare a global to hold the dataref:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">XPLMDataRef l4m;<\/span><\/pre>\n<div><\/div>\n<div>In the enable method, I added some code to find a dataref (one I was struggling with on ExtPlane\/Zibo):<\/div>\n<div><span style=\"font-family: 'courier new', courier, monospace;\">\u00a0<\/span><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\"> \/\/ find a dataref<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\"> l1m = XPLMFindDataRef (\"laminar\/B738\/fmc1\/Line01_M\");<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\"> if (l1m != NULL) {<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \/\/ check its datatype<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  printf (\"Found laminar\/B738\/fmc1\/Line01_M [type %d]\", XPLMGetDataRefTypes (l1m));<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\"> } else {<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  printf (\"Couldn't find laminar\/B738\/fmc1\/Line01_M\");<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\"> }<\/span><\/pre>\n<div><\/div>\n<div>Now in our flight_loop_callback, let&#8217;s get the value of this dataref:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">if (l1m != NULL) {<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  char buffer [40];<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  int c = XPLMGetDatab (<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 l1m,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 buffer,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 0,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 40<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  );<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  buffer[c] = '';<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  printf (\"Got %d [%s]\", c, buffer);<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\"> }<\/span><\/pre>\n<div><\/div>\n<div>When I first ran this &#8230; it couldn&#8217;t find the dataref. The reason for this that zibo&#8217;s FMC is an aircraft plugin, which means it is initialized when the aircraft is first loaded. However, Hello-World-SDK3 is a global plugin, and gets loaded way before that. Therefore, Zibo&#8217;s plugins aren&#8217;t available during the time the Hello-World-SDK3&#8217;s XPluginEnable method is called.<\/div>\n<div><\/div>\n<div>I modified it by moving the above code into flight_loop_callback():<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">PLUGIN_API float flight_loop_callback (<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  float inElapsedSinceLastCall,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  float inElapsedTimeSinceLastFlightLoop,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  int inCounter,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  void * inRefcon) {<\/span>\n\n<span style=\"font-family: 'courier new', courier, monospace;\">  printf (\"SHA: in flight_loop_callbackn\");<\/span>\n\n<span style=\"font-family: 'courier new', courier, monospace;\">  if (l4m == NULL) {<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 \/\/ find a dataref<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 l4m = XPLMFindDataRef (\"laminar\/B738\/fmc1\/Line01_M\");<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 if (l4m != NULL) {<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 \u00a0 \/\/ check its datatype<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 \u00a0 printf (\"Found laminar\/B738\/fmc1\/Line01_M [%d]n\", XPLMGetDataRefTypes (l4m));<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 } else {<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 \u00a0 printf (\"Couldn't find laminar\/B738\/fmc1\/Line01_Mn\");<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 }<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  }<\/span>\n\n<span style=\"font-family: 'courier new', courier, monospace;\">  if (l4m != NULL) {<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 char buffer [40];<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 int c = XPLMGetDatab (<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 \u00a0 l4m,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 \u00a0 buffer,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 \u00a0 0,<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 \u00a0 40<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 );<\/span>\n\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 buffer[c] = '';<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  \u00a0 printf (\"Got %d [%s]\", c, buffer);<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">  }<\/span>\n\n<span style=\"font-family: 'courier new', courier, monospace;\">  return 1.0;<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">};<\/span>\n\n\n<\/pre>\n<div>Now, when the aircraft loads and the sim proper starts, I get:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">Got 1 [ ]SHA: in flight_loop_callback<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">Got 1 [ ]SHA: in flight_loop_callback<\/span><\/pre>\n<div><\/div>\n<div>and after fiddling with the FMC:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">Got 24 [WMKC ]SHA: in flight_loop_callback<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">Got 24 [WMKC ]SHA: in flight_loop_callback<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">Got 24 [WMKC ]SHA: in flight_loop_callback<\/span><\/pre>\n<div><\/div>\n<div>Interestingly, my parallel ExtPlane session returned:<\/div>\n<div><\/div>\n<pre><span style=\"font-family: 'courier new', courier, monospace;\">RX 34 [ub laminar\/B738\/fmc1\/Line01_M Vw==]<\/span>\n<span style=\"font-family: 'courier new', courier, monospace;\">ZiboFMC got [laminar\/B738\/fmc1\/Line01_M|W]<\/span>\u00a0\u00a0<i>&lt;--- the last 3 letters are missing<\/i><\/pre>\n<div><\/div>\n<div>So it seems there&#8217;s some weirdness going on in ExtPlane, which is affecting my FMC.<\/div>\n<div><\/div>\n<div>Anyway, I removed the window drawing parts and just left my changes in the plugin, and the source can be found <a href=\"https:\/\/github.com\/dotsha747\/XPlane-WS-Plugin\/tree\/0a65715ca5c6ea988c54328822e0e9e63fe0747d\">here<\/a>.<\/div>\n<div><\/div>\n<div>I&#8217;ve not decided which way to go next; whether to use what I&#8217;ve learnt here and start debugging ExtPlane to see what&#8217;s going wrong; or &#8230; start writing my own plugin for the FMC.\u00a0 Writing my own plugin would be the more <i>educational<\/i> route, although I would probably end up replicating most of ExtPlane.<\/div>\n<div><\/div>\n<div>Checklist of stuff to learn to be able to write my own &#8220;dataref accessor&#8221; plugin:<\/div>\n<ul>\n<li>How to compile the plugin source on three platforms. Linux I have access to and familiarity with, and I can probably get a compiler going on Windows. But I don&#8217;t have access to a Mac (a VM Hackintosh maybe?). It&#8217;s a very interesting challenge though, especially to get a decent &#8220;workflow&#8221; going to build software this way.<\/li>\n<li>Running a separate thread to offload the bulk of the work out of X-Plane&#8217;s callbacks. Fortunately things are much simpler than they were 10-15 years ago, as C++11 now has threads. I&#8217;m assuming it would be easy to create cross-platform threaded code?<\/li>\n<li>Comms &#8230; I love BSD sockets on UNIX, but probably Boost::ASIO is a better cross-platform solution? I&#8217;m also curious about using the new Boost::Beast WebSockets library. It supports framing messages within the comms.<\/li>\n<li>Encoding &#8230; it would be more efficient to index the dataref names, like the X-PLane UDP API. And a lot more efficient to send binary data over the wire. But it&#8217;s also tempting to have a JSON encoded gateway to X-Plane&#8217;s internals (javascript dashboards?). Or perhaps I could support both binary and JSON?<\/li>\n<\/ul>\n<p><i>Originally created with EverNote at 20180127T093712Z<\/i><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been stumped for the last few weeks with getting my Raspberry Pi FMC to work with Zibo&#8217;s 737. I used the ExtPlane plugin to interface my code on the Pi with the simulator,&#46;&#46;&#46;<\/p>\n","protected":false},"author":1,"featured_media":9076,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[215],"tags":[84],"class_list":["post-8443","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-x-plane","tag-include"],"_links":{"self":[{"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=\/wp\/v2\/posts\/8443","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=8443"}],"version-history":[{"count":3,"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=\/wp\/v2\/posts\/8443\/revisions"}],"predecessor-version":[{"id":9078,"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=\/wp\/v2\/posts\/8443\/revisions\/9078"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=\/wp\/v2\/media\/9076"}],"wp:attachment":[{"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=8443"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=8443"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.shahada.abubakar.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=8443"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}