<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: onichandame</title>
    <description>The latest articles on DEV Community by onichandame (@onichandame).</description>
    <link>https://dev.to/onichandame</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F369007%2F93c71926-0e60-4d7a-91ad-37c0cc062e80.jpeg</url>
      <title>DEV Community: onichandame</title>
      <link>https://dev.to/onichandame</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/onichandame"/>
    <language>en</language>
    <item>
      <title>Blog made by Gatsby</title>
      <dc:creator>onichandame</dc:creator>
      <pubDate>Wed, 27 May 2020 01:25:43 +0000</pubDate>
      <link>https://dev.to/onichandame/blog-made-by-gatsby-58pj</link>
      <guid>https://dev.to/onichandame/blog-made-by-gatsby-58pj</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Background&lt;/li&gt;
&lt;li&gt;
Challenges

&lt;ul&gt;
&lt;li&gt;Static Site Generation&lt;/li&gt;
&lt;li&gt;Internationalization&lt;/li&gt;
&lt;li&gt;Gatsby Plugin i18n&lt;/li&gt;
&lt;li&gt;Translate Myself&lt;/li&gt;
&lt;li&gt;Markdown and JSX&lt;/li&gt;
&lt;li&gt;GraphQL&lt;/li&gt;
&lt;li&gt;Heading Anchor&lt;/li&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Implementation

&lt;ul&gt;
&lt;li&gt;Gatsby Build System&lt;/li&gt;
&lt;li&gt;GraphQL&lt;/li&gt;
&lt;li&gt;Internationalization&lt;/li&gt;
&lt;li&gt;Page Level&lt;/li&gt;
&lt;li&gt;Field Level&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having read &lt;a href="https://bloggingexplorer.com/self-hosted-blog"&gt;this blog&lt;/a&gt;, I am convinced to host my personal blog. The most convncing point is that self-hosting blogs give more ways to monetize. Every developer is keen on monetizing his/her skills, just like me.&lt;/p&gt;

&lt;p&gt;So the strategy has been made. Only tactics left undecided. There are 2 ways to build a self-hosted blog in general: using a blog builder such as WordPress, or build from "scratch". The difference between these 2 options is not actually well defined. I personally consider a tool requiring not web development skills as a blog builder.&lt;/p&gt;

&lt;p&gt;As a half-frontend-developer, I decided to use a proper develper's framework for the job: &lt;a href="https://www.gatsbyjs.org"&gt;Gatsby&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.gatsbyjs.org"&gt;Gatsby&lt;/a&gt; is a multi-page website generator based on React.js . The stack can be visualized as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2QRSDQSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sg2usaqpsouq03tawd5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2QRSDQSe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sg2usaqpsouq03tawd5d.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It can be seen that to use Gatsby, the user is expected to know HTML, CSS, JS, TS and React. Thus it is a great opportunity to self-learn frontend skills.&lt;/p&gt;

&lt;h1&gt;
  
  
  Challenges
&lt;/h1&gt;

&lt;p&gt;Before diving into this project, I had no knowledge of Gatsby as a framework. Thus the path I took to learn is also applicable for any other newbie developer who wish to learn Gatsby.&lt;/p&gt;

&lt;h2&gt;
  
  
  Static Site Generation
&lt;/h2&gt;

&lt;p&gt;As a Next.js developer, I am used to use server-side code during runtime. However it is forbidden for Gatsby as a Static Site Generator(SSG). This descrepancy has lead me to a dead end for many times. So these are the differences between SSG and SSR(Server Side Rendering):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SSR&lt;/th&gt;
&lt;th&gt;SSG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;allow server side code at runtime&lt;/td&gt;
&lt;td&gt;does not allow server side code at runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;shipped with server executable&lt;/td&gt;
&lt;td&gt;shipped as plain html/css/... files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dynamically generate pages&lt;/td&gt;
&lt;td&gt;requires rebuild to change page content&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Based on the principle of SSG, 3 baselines need to be followed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;separate codes for buildtime and code for runtime&lt;/li&gt;
&lt;li&gt;write runtime code as if writing in browser's dev tool&lt;/li&gt;
&lt;li&gt;make sure all data can be statically fetched at buildtime&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Internationalization
&lt;/h2&gt;

&lt;p&gt;Working in a non-English-speaking country, it is necessary to serve audience from both English background and Chinese background. Thus my blog needs to serve contents in both English and Chinese. This is one of the considerations I took when I decided to make my own blogger as no other blogger is popular in both China and the outside world.&lt;/p&gt;

&lt;p&gt;Coming to the implementation part, there are several tools and examples available online that can guide me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gatsby Plugin i18n
&lt;/h3&gt;

&lt;p&gt;The first approach I tried was using a gatsby plugin. This is the best option for a non-blog site. I have used a similar tool for Next.js so this is not too hard to catch up. However this tool does not suit the needs of a blog site as it only provides field level translation. A blog utilizing field level translation is extremely difficult to maintain as all translation sources are kept in JSON files. A blog stored in JSON? No...&lt;/p&gt;

&lt;h3&gt;
  
  
  Translate Myself
&lt;/h3&gt;

&lt;p&gt;According to &lt;a href="https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n/"&gt;the official guide&lt;/a&gt;, it is possible to achieve field level translation and page level translation at the same time.&lt;/p&gt;

&lt;p&gt;Based on the guide, I made a &lt;a href="https://github.com/onichandame/gatsby-template"&gt;template&lt;/a&gt; that bundles gatsby with mdx, page level translation and field level translation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Markdown and JSX
&lt;/h2&gt;

&lt;p&gt;The most significant difference between a blog and a regular site is that blogs are served in virtually the same layout. For the ease of maintenance, it would be better to keep the source of the blogs in a stable file format that has not changed in the past 10 years. It would also be beneficial if the source and the output are very similar so that overhead is small, and it is visually clear during writing.&lt;/p&gt;

&lt;p&gt;Combining the above considerations, markdown is the best option. Markdown standard is very stable. Although some improvements have been added in the past few years, all plugins keep a very good interoperability and good backward compatibility.&lt;/p&gt;

&lt;p&gt;However, the original markdown is quite limited in terms of functionality. It's implicity constrains its potential, especially for the flexbility of UI. Hence an enhancement needs to be made.&lt;/p&gt;

&lt;p&gt;JSX is a great tool for creating fancy UI. Using markdown as the backbone and occasionally decorated with JSX will provide a decent layout for blogs. Thus &lt;code&gt;gatsby-plugin-mdx&lt;/code&gt; is chosen.&lt;/p&gt;

&lt;p&gt;Having decided to adopt mdx, several issues came in the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL
&lt;/h3&gt;

&lt;p&gt;mdx in Gatsby allows page query. However, I personally do not like to use page query. Moreover, it is not usable in non-page components, which significantly limits its usage.&lt;/p&gt;

&lt;p&gt;Sadly, component level query is not available in mdx as the JSX components in mdx are not compiled to retrieve static data during build time. Therefore the codes like &lt;code&gt;useStaticQuery&lt;/code&gt; is left to run in runtime, where the query will not succeed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Heading Anchor
&lt;/h3&gt;

&lt;p&gt;A very useful markdown enhancement is the anchor of the headings. This is one of the basic requirements for table of contents. However, &lt;code&gt;gatsby-plugin-mdx&lt;/code&gt; is not shipped with this support.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://johno.com/mdx-table-of-contents-components-in-gatsby"&gt;this blog&lt;/a&gt;, this feature can be easily added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;The last problem is to host the site. There are generally 3 methods to choose:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;buy a managed hosting service&lt;/li&gt;
&lt;li&gt;host on a self-managed server&lt;/li&gt;
&lt;li&gt;find a free hosting service&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The option 1 and 2 are for lazy users who do not want to spend time digging the internet. I happen to know a hosting service for free: Github Pages.&lt;/p&gt;

&lt;p&gt;Github Pages can be easily set up following the official guide. Bear in mind that there are 2 types of Page: project and personal. The one used for the purpose of this blog is the personal one.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;Here I record the detailed implementation of the blog site I made. Basic knowledge of React.js is preassumed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gatsby Build System
&lt;/h2&gt;

&lt;p&gt;The first thing to learn is the build system of gatsby.&lt;/p&gt;

&lt;p&gt;The source code is written in JSX and the output is static website. There are several fundamental issues here, most have been addressed by Gatsby itself, such as client side routing. One thing that requires special care is the data retrieval.&lt;/p&gt;

&lt;p&gt;People from the background of server side coding may intuitively write codes that are left to run on server during runtime. This is a big no when using Gatsby, or any other SSG tool. Gatsby only allows 2 runtimes: buildtime and browser runtime.&lt;/p&gt;

&lt;p&gt;During buildtime, all data are retrieved to create static HTML. During browser runtime, only browser API is available.&lt;/p&gt;

&lt;p&gt;The codes for buildtime reside in the following locations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;page query and static query(GraphQL queries are run once at buildtime)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gatsby-node.js&lt;/code&gt;, &lt;code&gt;gatsby-config.js&lt;/code&gt;, &lt;code&gt;gatsby-browser.js&lt;/code&gt; and &lt;code&gt;gatsby-ssr.js&lt;/code&gt; under the project's root directory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All other codes are compiled to static HTML at buildtime.&lt;/p&gt;

&lt;p&gt;For details of the &lt;code&gt;gatsby-node.js&lt;/code&gt; etc. see &lt;a href="https://www.gatsbyjs.org/docs/conceptual-guide/"&gt;the conceptual guid&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL
&lt;/h2&gt;

&lt;p&gt;The usage of GraphQL in Gatsby is somewhat unusual. In traditional data retrieval modes, a client fires a query to a backend API and the backend respond with the required data. Naively I thought it happens during runtime. But Gatsby runs all GraphQL queries during buildtime. Then all the queries are stripped away from the output.&lt;/p&gt;

&lt;p&gt;Therefore all the queries must be given all the required data and schema during buildtime. One of the main sources of data is &lt;code&gt;gatsby-config.js&lt;/code&gt; and &lt;code&gt;gatsby-node.js&lt;/code&gt;. check &lt;a href="https://www.gatsbyjs.org/docs/conceptual-guide/"&gt;the guide&lt;/a&gt; for details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Internationalization
&lt;/h2&gt;

&lt;p&gt;To unify page level and field level internationalization, the following design is made.&lt;/p&gt;

&lt;h3&gt;
  
  
  Page Level
&lt;/h3&gt;

&lt;p&gt;Only mdx files are accepted for page level translation, such as posts and resume page.&lt;/p&gt;

&lt;p&gt;The example source structure of a post is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ou6nQ7t4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zu94qdhchrge26yscsew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ou6nQ7t4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zu94qdhchrge26yscsew.png" alt="i18n"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All translations of the page are kept in one folder, which is named as the slug of the post. The file name represents the locales. All the information is extracted by codes in &lt;code&gt;gatsby-node.js&lt;/code&gt; then is passed down to the corresponding pages.&lt;/p&gt;

&lt;p&gt;As all the pages have the same layout, a template is passed to the &lt;code&gt;gatsby-plugin-mdx&lt;/code&gt; that is applied to all post pages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Field Level
&lt;/h3&gt;

&lt;p&gt;Field level translations are kept in a similar structure, where the file name represents the locale. A custom hook is used to retrieve those translations.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://onichandame.com"&gt;my blog&lt;/a&gt; for the final result!&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>react</category>
      <category>blog</category>
    </item>
    <item>
      <title>CentOS 8 Bluetooth Headset</title>
      <dc:creator>onichandame</dc:creator>
      <pubDate>Thu, 21 May 2020 01:47:39 +0000</pubDate>
      <link>https://dev.to/onichandame/centos-8-bluetooth-headset-1c26</link>
      <guid>https://dev.to/onichandame/centos-8-bluetooth-headset-1c26</guid>
      <description>&lt;p&gt;On a fresh-new CentOS 8 desktop, it is not trivial to get a Bluetooth headset connected for the first time.&lt;/p&gt;

&lt;h1&gt;
  
  
  Symptoms
&lt;/h1&gt;

&lt;p&gt;When trying to connect to a paired headset in Bluetooth settings, it keeps failing but not error log can be seen. The logs can be retrieved by running command &lt;code&gt;journalctl -xe&lt;/code&gt; and scroll up to find the read lines similar to &lt;code&gt;a2dp-sink profile connect failed for 5C:C6:E9:B9:9D:DE: Protocol not available&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This error message tells that the bluetooth service successfully identifies the headset, but having trouble to hand over the connection to the audio service.&lt;/p&gt;

&lt;h1&gt;
  
  
  Fix
&lt;/h1&gt;

&lt;p&gt;The most common audio service shipped with CentOS is pulseaudio. The quick fix is to restart the pulseaudio service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;pulseaudio &lt;span class="nt"&gt;-k&lt;/span&gt;
pulseaudio &lt;span class="nt"&gt;--start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that both commands are meant to be run as non-root user.&lt;/p&gt;

&lt;h1&gt;
  
  
  Credits
&lt;/h1&gt;

&lt;p&gt;Thanks to &lt;a href="https://unix.stackexchange.com/questions/258074/error-when-trying-to-connect-to-bluetooth-speaker-org-bluez-error-failed"&gt;this question&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bluetooth</category>
    </item>
    <item>
      <title>Rootless systemd and podman</title>
      <dc:creator>onichandame</dc:creator>
      <pubDate>Mon, 18 May 2020 06:34:11 +0000</pubDate>
      <link>https://dev.to/onichandame/rootless-systemd-2ofd</link>
      <guid>https://dev.to/onichandame/rootless-systemd-2ofd</guid>
      <description>&lt;p&gt;Systemd is a useful tool programmatically managing routine tasks. I encountered a specific situation with some constraints:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;start task on boot as a non-root user&lt;/li&gt;
&lt;li&gt;auto restart on failure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The solution to this issue is to create a systemd task as the non-root user.&lt;/p&gt;

&lt;h1&gt;
  
  
  Steps
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Create the service
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl edit &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--full&lt;/span&gt; &lt;span class="nt"&gt;--force&lt;/span&gt; &amp;lt;service_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  add the content
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=&amp;lt;Service&amp;gt;

[Service]
Restart=always
ExecStart=&amp;lt;Servece_Start&amp;gt;
ExecStop=&amp;lt;Service_Stop&amp;gt;

[Install]
WantedBy=default.target
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;the last line registers the service to the dependency list for auto-start&lt;/p&gt;

&lt;h2&gt;
  
  
  enable the service
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &amp;lt;service_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  (optional) enable linger
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;loginctl enable-linger &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is to allow the service continuing to run after the user is logged out.&lt;/p&gt;

&lt;h1&gt;
  
  
  Podman Service
&lt;/h1&gt;

&lt;p&gt;When running podman container as a systemd service, the &lt;code&gt;ExecStart&lt;/code&gt; command should be &lt;code&gt;podman start -a &amp;lt;container_name&amp;gt;&lt;/code&gt; and the &lt;code&gt;ExecStop&lt;/code&gt; command should be &lt;code&gt;podman stop -t &amp;lt;seconds_to_wait&amp;gt; &amp;lt;container_name&amp;gt;&lt;/code&gt;. Details can be found at &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux_atomic_host/7/html/managing_containers/running_containers_as_systemd_services_with_podman"&gt;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux_atomic_host/7/html/managing_containers/running_containers_as_systemd_services_with_podman&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Credits
&lt;/h1&gt;

&lt;p&gt;Thanks to &lt;a href="https://github.com/systemd/systemd/issues/2690"&gt;this issue&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>systemd</category>
    </item>
    <item>
      <title>ulimit on CentOS 8.1</title>
      <dc:creator>onichandame</dc:creator>
      <pubDate>Wed, 13 May 2020 08:53:34 +0000</pubDate>
      <link>https://dev.to/onichandame/ulimit-on-centos-8-1-3md3</link>
      <guid>https://dev.to/onichandame/ulimit-on-centos-8-1-3md3</guid>
      <description>&lt;h1&gt;
  
  
  Problem
&lt;/h1&gt;

&lt;p&gt;On a fresh-installed Centos 8.1(kernel 4.18.0-147.8.1.el8_1) machine, the max number of opened file descriptors is set to H: 2048, S: 1024.&lt;/p&gt;

&lt;p&gt;By editing &lt;code&gt;/etc/security/limits.conf&lt;/code&gt; the limits can be upped. But this only takes effect in non-gnome-terminals, such as ssh sessions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cause
&lt;/h1&gt;

&lt;p&gt;The gnome terminals do not see &lt;code&gt;/etc/security/limits.conf&lt;/code&gt; during login as they are started under systemd(I hate it).&lt;/p&gt;

&lt;h1&gt;
  
  
  Fix
&lt;/h1&gt;

&lt;p&gt;According to &lt;a href="https://www.linuxquestions.org/questions/linux-server-73/increasing-ulimit-open-files-not-applied-in-rhel-8-1-a-4175672919/"&gt;this&lt;/a&gt;, &lt;code&gt;/etc/systemd/user.conf&lt;/code&gt; sets the soft limit and &lt;code&gt;/etc/systemd/system.conf&lt;/code&gt; sets the hard limit for the user under systemd. The variable for file descriptors is &lt;code&gt;DefaultLimitNOFILE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you are using Node.js, one more step is required: &lt;code&gt;echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf &amp;amp;&amp;amp; sudo sysctl -p&lt;/code&gt; according to &lt;a href="https://github.com/gatsbyjs/gatsby/issues/11406"&gt;this issue&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>centos</category>
      <category>linux</category>
      <category>ulimit</category>
    </item>
    <item>
      <title>Collaboration using Blender 3D</title>
      <dc:creator>onichandame</dc:creator>
      <pubDate>Sat, 09 May 2020 03:37:59 +0000</pubDate>
      <link>https://dev.to/onichandame/collaboration-using-blender-3d-5a5</link>
      <guid>https://dev.to/onichandame/collaboration-using-blender-3d-5a5</guid>
      <description>&lt;p&gt;The Blender 3D is the best 3D modelling software on Linux. I use Linux for work, and I need to process the 3D models made by my colleagues. Hence I have to learn to use Blender.&lt;/p&gt;

&lt;h1&gt;
  
  
  Scenario
&lt;/h1&gt;

&lt;p&gt;There are 2 constraints in my situation, which are quite common I guess:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;models are made in SolidWorks 2018&lt;/li&gt;
&lt;li&gt;colleagues do not have any knowledge of Linux, open-source 3D model formats or any other thing that a non-IT worker is not assumed to know.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Problems
&lt;/h1&gt;

&lt;p&gt;In this section, I will record all the important issues solved during my work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-language
&lt;/h2&gt;

&lt;p&gt;The working environment of my colleagues is in Chinese. So inevitably they export files with non-English characters.&lt;/p&gt;

&lt;p&gt;Blender's default setting disables the multi-language support. In order to display non-English characters on the UI, one has to check the box next to the &lt;em&gt;Translation&lt;/em&gt; tab in the &lt;em&gt;Preferences&amp;gt;Interface&lt;/em&gt; dialogue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cpo8m7Do--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w66vsrftf9ltlsl9we66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cpo8m7Do--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w66vsrftf9ltlsl9we66.png" alt="chinese"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  File Format
&lt;/h2&gt;

&lt;p&gt;The biggest challenge is to move the models from SolidWorks to Blender. Thankfully they both support a wide range of file formats for import/export. Hence the quickest solution is to find a format to which SolidWorks can export, and from which Blender can import.&lt;/p&gt;

&lt;p&gt;The best open source format in my mind is GLTF. Unfortunately, SolidWorks only began to support GLTF since ver. 2019.&lt;/p&gt;

&lt;p&gt;After a quick search, 2 formats were found to suit my needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;STL&lt;/li&gt;
&lt;li&gt;WRL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WRL file exported from SolidWorks cannot be properly imported to Blender. Nothing on the canvas is shown after the import finishes. The scene collection does not show any change either.&lt;/p&gt;

&lt;p&gt;So STL is the only choice left. The bad thing about it is that SolidWorks exports many different files, each responsible for one component. But this is not a major bug which prevents using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  View Clipping
&lt;/h2&gt;

&lt;p&gt;This problem is not collaboration related but general for any usage of Blender.&lt;/p&gt;

&lt;p&gt;If you know the basics of the camera parameters in 3D modelling, viewing frustum should not be too alienated. Basically in a virtual world composed of 3D models, you are only presented with a finite volume. This constraint is to prevent infinity. The farthest plane orthogonal to your viewing vector is one of the constraints. Anything beyond that plane is not rendered. This parameter defaults to a quite small value. I had to change it as the model imported is very large. Press "N" key to open the following dialogue and change the Clip Start and End value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hqpbXNa3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zqoq82zjedecj7b4ywqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hqpbXNa3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zqoq82zjedecj7b4ywqp.png" alt="clipping"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blender</category>
      <category>3d</category>
      <category>modeling</category>
    </item>
    <item>
      <title>EPICS Deployment</title>
      <dc:creator>onichandame</dc:creator>
      <pubDate>Fri, 08 May 2020 11:20:17 +0000</pubDate>
      <link>https://dev.to/onichandame/epics-deployment-140e</link>
      <guid>https://dev.to/onichandame/epics-deployment-140e</guid>
      <description>&lt;p&gt;As a newbie in the world of EPICS and hardware control system, every day many new challenges come and many are solved. This is a record of the experience so that when the same problem comes in the future, I don't need to get through it again.&lt;/p&gt;

&lt;h1&gt;
  
  
  System Architecture
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tdY7e46j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fy3lrcw0frml7idw5o6u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tdY7e46j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fy3lrcw0frml7idw5o6u.png" alt="arch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The details of the architecture are not covered here. Broadly speaking, the proxy converts EPICS protocol to GraphQL or vice versa.&lt;/p&gt;

&lt;h1&gt;
  
  
  Challenges
&lt;/h1&gt;

&lt;p&gt;This section records all major challenges I have met. Some are trivial but I was not smart enough to find a solution immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connection between Different Hosts
&lt;/h2&gt;

&lt;p&gt;The connection between CA client and IOC server is the most anti-intuitive part for me. EPICS provides a convenient technology "UDP broadcasting", which helps establish connection when both parties have no knowledge of the IP address of each other.&lt;/p&gt;

&lt;p&gt;However, This method only works as expected when both parties are in the same and the only subnet. e.g. when the CA client has 2 IP addresses in 2 different subnets, such as 10.0.0.1 and 192.168.1.1, it is not guaranteed to be able to find an IOC whose address is 192.168.1.100.&lt;/p&gt;

&lt;p&gt;When no better architecture design is feasible, one has to provide the IP address of the IOC server to the CA client, so that the client knows where to look for the IOC. The way to tell is by setting an environmental variable &lt;code&gt;EPICS_CA_ADDR_LIST&lt;/code&gt; when starting the CA client. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EPICS_CA_ADDR_LIST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.100
&lt;span class="c"&gt;# CA connect here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;However, it is generally a better practice to minimize the necessary configurations. A better solution here is putting the CA client in the subnet where IOC servers are found and no other subnets. In my architecture, it means that the web services and IOCs, as well as the proxy, are in the same subnet. This is achievable when the number of IOCs is relatively small. When there is large number of IOCs, they must be divided into different subnets for better management. In that case, a workaround can be made by introducing another layer of proxy between web services and the EPICS proxy, so that the EPICS proxy can stay with 1 IP.&lt;/p&gt;

</description>
      <category>web</category>
      <category>epics</category>
      <category>ioc</category>
      <category>proxy</category>
    </item>
    <item>
      <title>React-based 3D components</title>
      <dc:creator>onichandame</dc:creator>
      <pubDate>Sat, 02 May 2020 08:29:15 +0000</pubDate>
      <link>https://dev.to/onichandame/react-based-3d-components-13h8</link>
      <guid>https://dev.to/onichandame/react-based-3d-components-13h8</guid>
      <description>&lt;p&gt;This is a record of my personal experience on the 3D React component development. The content may be updated at any time because the project is still under development.&lt;/p&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;I need to design and build a 3D based building information modelling(BIM) due to the nature of BIM. This BIM component needs to reside in an existing system based on Next.js, a UI framework based on React and specialized in SSR optimization.&lt;/p&gt;

&lt;h1&gt;
  
  
  Demand Analysis
&lt;/h1&gt;

&lt;h2&gt;
  
  
  End-User Experience
&lt;/h2&gt;

&lt;p&gt;The end user is an untrained worker who is not specialzed in either IT or BIM. Therefore the functions are simple and understandable for every computer user.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An intrinsic mapping between the UI and the real building, down to the component level&lt;/li&gt;
&lt;li&gt;An intrinsic interactive pattern based on ray casting&lt;/li&gt;
&lt;li&gt;All texts and numbers on the UI must be clear and minimal&lt;/li&gt;
&lt;li&gt;A camera reset button, a scene reload button and a virtual button on each component to enter the corresponding page of details&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Technical Constraints
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Must be integrated into a Next.js project&lt;/li&gt;
&lt;li&gt;Must be able to load 3D models like GLTF and OBJ&lt;/li&gt;
&lt;li&gt;Must be code-splitted to minimize the impact on the performance of the original website&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Technology Analysis
&lt;/h1&gt;

&lt;p&gt;Based on the demand analysis, I conducted research to find some existing technologies that I can make use of. Many good frameworks are found but they all boils down to 3 fundamental technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  React 360
&lt;/h2&gt;

&lt;p&gt;This is a framework built by Facebook for VR development. It also supports 3D UI out of the box. By default it loads models from GLTF and OBJ formats. At the first glance it may be the best choice for the following reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;3D out of the box&lt;/li&gt;
&lt;li&gt;interactive out of the box&lt;/li&gt;
&lt;li&gt;supports open source models GLTF and OBJ&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But it is based on React Native, which makes it difficult to integrate into the existing webpage. Truly it can be embedded in &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;. But this workaround brings more complexities into the deployment phase. Moreover, this framework aims at VR, which is not a 100% overlap with BIM. Not to say that this framework is not as popular as its competitors, which means that it is more defficult to get help from the community.&lt;/p&gt;

&lt;h2&gt;
  
  
  Babylon
&lt;/h2&gt;

&lt;p&gt;This framework supports the integration with React officially, according to the &lt;a href="https://doc.babylonjs.com/resources/babylonjs_and_reactjs"&gt;official docs&lt;/a&gt;. However the docs also mentions that there may be performance overhead if used with React. The optimal choice is to use the pure JavaScript, which is what I would like to avoid. There are feasible ways such as React DOM or reconciler. But again the community is quite small. &lt;a href="https://github.com/brianzinn/react-babylonjs"&gt;The existing solution&lt;/a&gt; has just 140 stars on GitHub at the time of writing this sentence. As an individual developer the size of community is at the top of my list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three-based Solution
&lt;/h2&gt;

&lt;p&gt;Now the main course comes. Three.js is the most popular 3D framework in Web development. It has the largest most active community. However, it is not designed for React hence the React-Three community is not comparable to the entire Three community.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.npmjs.com/package/react-three-fiber"&gt;React-Three-Fiber&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This is the most suitable framework in this condition.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deep integration with React with no significant performance overhead&lt;/li&gt;
&lt;li&gt;Able to load GLTF models using useLoader hook, GLTF loader from Three and React Suspense&lt;/li&gt;
&lt;li&gt;Just a wrapper around three so info from three community is most likely helpful&lt;/li&gt;
&lt;li&gt;Many helper packages from the same team that help ray casting, animation and more.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The only hack needed here is the dynamic import of Next.js with SSR disabled. It is not a traditional hack that needs a proper fix, but a non-intuitive but reasonable solution for the problem: 2D rendering is compatible with SSR as SSR produces plain HTML, but 3D requires runtime loading of the resources such as models. If models are wrapped in React components, these components must be dynamically loaded using &lt;code&gt;next/dynamic&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, dynamic importing of Next.js provides code-splitting out of the box.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;Currently, only the feasibility analysis is done. The package has not been designed yet.&lt;/p&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>An EPICS Proxy</title>
      <dc:creator>onichandame</dc:creator>
      <pubDate>Mon, 20 Apr 2020 07:45:43 +0000</pubDate>
      <link>https://dev.to/onichandame/an-epics-proxy-3oh1</link>
      <guid>https://dev.to/onichandame/an-epics-proxy-3oh1</guid>
      <description>&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://epics-controls.org/"&gt;EPICS&lt;/a&gt; is a well-known framework for controlling a wide range of hardware. Just like other inventions from the particle physicists, it reaches beyond the field of the particle physics experiments.&lt;/p&gt;

&lt;h1&gt;
  
  
  Scenario
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://epics-controls.org/"&gt;EPICS&lt;/a&gt; provides a comprehensive bucket of tools for almost all use cases. In the age of the internet, however, many unprecedented demands have emerged. One of which is to allow remote control over the internet.&lt;/p&gt;

&lt;p&gt;Here comes a trivial problem that has never been addressed before: how to communicate as a web front with the EPICS IOCs?&lt;/p&gt;

&lt;h1&gt;
  
  
  Technical Consideration
&lt;/h1&gt;

&lt;p&gt;As a never-before new system, one of the most fundamental engineering principles is to stick with the existing standards as much as possible. Based on this principle, 3 sub-principles must be followed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;use loosely-coupled microservices each provides a limited and well-defined functionality&lt;/li&gt;
&lt;li&gt;communication protocols and data structure are kept simple and consistent across the smallest necessary scope.&lt;/li&gt;
&lt;li&gt;use standardized protocols, deployment workflow, and libraries&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hence a bridge between IOCs of the 20th century and the web frameworks of the 21st century is necessary.&lt;/p&gt;

&lt;h1&gt;
  
  
  Design
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Data Flow
&lt;/h2&gt;

&lt;p&gt;The data flow is the foundation of any software system and should be drafted before the actual development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RrOHvzYN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/b7oijip5waywh4ls464z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RrOHvzYN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/b7oijip5waywh4ls464z.png" alt="Basic structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication Protocol
&lt;/h2&gt;

&lt;p&gt;According to the basic structure, the proxy needs to handle 2 types of protocols: one for the microservices and one for the IOCs. The former has many standards I can choose from. The latter only has two standards at the moment: CA and PV.&lt;/p&gt;

&lt;p&gt;As PV is only available to EPICS 7+, for better compatibility of the proxy, CA should be supported first.&lt;/p&gt;

&lt;p&gt;Considering that &lt;code&gt;caget&lt;/code&gt; &lt;code&gt;caput&lt;/code&gt; are compatible with the stateless protocols, but &lt;code&gt;camonitor&lt;/code&gt; requires a stateful protocol like WebSocket, the protocol to the microservices must support both cases. Hence the most popular communication protocol HTTP-based REST is not an option. After a quick research, GraphQL from Facebook is found to satisfy all the demands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework
&lt;/h2&gt;

&lt;p&gt;Both CA and GraphQL are too complicated to make from scratch. Therefore some existing frameworks must be utilized. Based on the choice of the protocols, 2 frameworks are required:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a GraphQL server&lt;/li&gt;
&lt;li&gt;a CA library&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The most popular GraphQL server is the apollo-server, and it is the only server that supports real-time subscriptions out of the box.&lt;/p&gt;

&lt;p&gt;The only CA library comes from EPICS-base, in the form of dynamic libraries and executable binaries.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;The development of the proxy was not smooth and simple, therefore I will divide this section into challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feasibility
&lt;/h2&gt;

&lt;p&gt;The very first question is: is such proxy even possible?&lt;/p&gt;

&lt;p&gt;This question can be broken down into 2:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;is it possible to integrate CA library with GraphQL server?&lt;/li&gt;
&lt;li&gt;is the proxy able to communicate with the IOCs on the other hosts in the network?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first question is answered by &lt;a href="https://github.com/pyepics/pyepics"&gt;this tool&lt;/a&gt; and &lt;a href="https://github.com/RobbieClarken/node-epics"&gt;this tool&lt;/a&gt;. Thanks to the great pioneers who made these tools!&lt;/p&gt;

&lt;p&gt;The second question can be cleared by a simple test using the tools mentioned above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Apollo Server
&lt;/h2&gt;

&lt;p&gt;GraphQL is way more complicated than it sounds if you are new to it. There are many useful resources out there to help newbies set up their first server. I found &lt;a href="https://www.apollographql.com/docs/apollo-server/getting-started/"&gt;this&lt;/a&gt; and &lt;a href="https://typegraphql.com/"&gt;this&lt;/a&gt; to be very helpful.&lt;/p&gt;

&lt;p&gt;The biggest challenge here is to understand the concept of the resolver. It is a standardized component of GraphQL so many different packages can work together without a problem. As I use TypeScript to code, &lt;a href="https://typegraphql.com/"&gt;type-graphql&lt;/a&gt; suits my needs best. Although a code-first GraphQL server is better, the Apollo server does not support the code-first approach.&lt;/p&gt;

&lt;p&gt;Having implemented &lt;code&gt;caget&lt;/code&gt;, &lt;code&gt;caput&lt;/code&gt; and &lt;code&gt;camonitor&lt;/code&gt; as &lt;code&gt;Query&lt;/code&gt;, &lt;code&gt;Mutation&lt;/code&gt; and &lt;code&gt;Subscription&lt;/code&gt; respectively, the server part is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to IOC
&lt;/h2&gt;

&lt;p&gt;Here comes the most tricky part. As Apollo Server only runs with Node.js, using JS/TS is the only choice. However, &lt;a href="https://github.com/RobbieClarken/node-epics"&gt;node-epics&lt;/a&gt; is too old to support the latest Node.js. Hence I made &lt;a href="https://github.com/onichandame/epics-ioc-connection/"&gt;my own fork&lt;/a&gt;. &lt;del&gt;Sadly it depends on &lt;code&gt;ref-napi&lt;/code&gt; which only works on node before 13. Therefore &lt;a href="https://github.com/onichandame/epics-ioc-connection/"&gt;it&lt;/a&gt; requires node &amp;lt;13, which puts this proxy to the same restriction. Luckily we have containers that minimize the impact of such restriction.&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;After some head-scratching, &lt;a href="https://github.com/onichandame/epics-ioc-connection/"&gt;this tool&lt;/a&gt; is published. The connection to the IOC is easily implemented and tested.&lt;/p&gt;

&lt;p&gt;This issue took me a week before the first version is published whereas the other issues only took me a couple of days.&lt;/p&gt;

&lt;h1&gt;
  
  
  Deployment
&lt;/h1&gt;

&lt;p&gt;As the proxy relies on a specific version of the Node, it is better shipped with the Node of the correct version. Hence docker is the best solution.&lt;/p&gt;

&lt;p&gt;Personally I use Kubernetes to manage the containers. I recommend anyone who needs docker to consider to switch to Kubernetes as it is just awesome.&lt;/p&gt;

&lt;h1&gt;
  
  
  Links
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/onichandame/epics-proxy/"&gt;click here&lt;/a&gt; for the proxy&lt;br&gt;
&lt;a href="https://github.com/onichandame/epics-ioc-connection/"&gt;click here&lt;/a&gt; for the JS binding of CA&lt;/p&gt;

</description>
      <category>epics</category>
      <category>node</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
