<?xml version="1.0" encoding="UTF-8"?>
<rss version='2.0' xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Marnee Dearman (KG7SIO)</title>
    <description>Full Stack .NET Programmer and Ham</description>
    <link>https://marnee.silvrback.com/feed</link>
    <atom:link href="https://marnee.silvrback.com/feed" rel="self" type="application/rss+xml"/>
    <category domain="marnee.silvrback.com">Content Management/Blog</category>
    <language>en-us</language>
      <pubDate>Sat, 21 Mar 2020 18:50:29 -0700</pubDate>
    <managingEditor>marnee.dearman@gmail.com (Marnee Dearman (KG7SIO))</managingEditor>
      <item>
        <guid>https://marnee.silvrback.com/using-and-programming-an-ft-70#49257</guid>
          <pubDate>Sat, 21 Mar 2020 18:50:29 -0700</pubDate>
        <link>https://marnee.silvrback.com/using-and-programming-an-ft-70</link>
        <title>Using and programming an FT-70</title>
        <description>It&#39;s not as easy as it looks</description>
        <content:encoded><![CDATA[<h1 id="using-and-programming-an-ft-70">Using and programming an FT-70</h1>

<p>I recently got a Yaesu FT-70. I tried to program it. I tried using the radio to do this but OMG it was so hard. I kept getting a message like this when I tried to create a new channel: <code>NO BNK</code>. <code>NO BNK</code>? I&#39;m getting <code>PC LOAD LETTER</code> vibes from this. </p>

<p><img alt="I am so lost." src="http://www.dumpaday.com/wp-content/uploads/2013/05/i-have-no-idea-what-Im-doing-meme-4.jpg" /></p>

<p>Grr argh. So I bought the <a href="https://www.rtsystemsinc.com/FT-70D-programming-software-and-USB-cable-s/2699.htm">RT Systems FT-70 programming cable and software</a>, which came right away and just works. </p>

<blockquote>
<p>Props to RT Systems. It just works and is easy to use.</p>
</blockquote>

<p>It also took me a while to figure out how to:<br>
* Adjust the squelch<br>
* Change the transmission power<br>
* Switch to frequency mode, so I can manually change the frequency and tone code</p>

<h2 id="automatic-mode-selection-ams">Automatic Mode Selection (AMS)</h2>

<p>This is a nice feature. Let&#39;s say you are using a repeater that supports both analog and System Fusion (C4FM). Does this mean you won&#39;t hear any analog signals if you are in digital mode? Does this mean you won&#39;t hear any digital signals when you are in analog mode?</p>

<p>No!</p>

<p>With AMS, the radio will automagically select the mode for the incoming signal. You can turn this feature off and on with the <code>AMS</code> button.</p>

<p><img alt="It&#39;s magical" src="http://ministryoftype.co.uk/content/words/article/442-automagically/automagically.jpg" /></p>

<p>OK, here we go.</p>

<h2 id="operating-modes">Operating Modes</h2>

<p>The FT-70 supports a number of operating modes.</p>

<table><thead>
<tr>
<th>Mode</th>
<th>FT-70 Display Code</th>
</tr>
</thead><tbody>
<tr>
<td>FM</td>
<td>FM</td>
</tr>
<tr>
<td>Digital (System Fusion, aka C4FM)</td>
<td>DN</td>
</tr>
<tr>
<td>AM</td>
<td>AM</td>
</tr>
<tr>
<td>?? I wish I knew what this means</td>
<td>VW</td>
</tr>
</tbody></table>

<h2 id="memory-banks">Memory Banks</h2>

<p>Every channel you program into the radio can be assigned to one or more memory banks, and you can switch between memory banks to access that bank&#39;s list of channels. There are a lot of memory banks in this thing.</p>

<p><a href="https://www.youtube.com/watch?v=vkBacicG4H8">Watch this video for more information on memory banks.</a></p>

<p><img alt="It&#39;s magical" src="http://learningsuccess.com/memory_bank1.jpg" /></p>

<h2 id="programming-memory-channels-into-the-ft-70">Programming memory channels into the FT-70</h2>

<p>I RTFMed, I swear, but I lost the plot. <em>So many buttons</em>. With my Baofeng I could use CHIRP, but I wasn&#39;t so sure this would work with the FT-70 so I got the RT Systems programming cable and software to do it instead. It&#39;s a good chunk of change, but it just works. </p>

<blockquote>
<p>Always glad to support the Amateur Radio community when I can.</p>
</blockquote>

<p>If you are using RT Systems software, and are in the Southern Arizona area, you might like to use <a href="https://www.dropbox.com/sh/d136s9llot2zpz5/AAA9QmD3ZDYqu2Q4Q6omnFCNa?dl=0">this tab delimited flat file</a> to import into the RT Systems software to get all of the repeaters I like to use regularly, including all of the <a href="https://docs.google.com/spreadsheets/d/1yeM3pAbVwHcXSKxLBGXJxZfvbCELgGQF_LpYuD5amUA/">Oro Valley Amateur Radio Club&#39;s repeaters</a>, two of which support System Fusion, Yaesu&#39;s digital protocol. </p>

<blockquote>
<p>The repeater engineers in my club really like it. They say it is the most easy to setup and use for both the repeater engineers and the end users like me. I  you guys!</p>
</blockquote>

<p>If you are getting the <code>NO BNK</code> message while programming the radio, <a href="https://www.youtube.com/watch?v=vkBacicG4H8">watch this video</a> to understand what the memory banks are and how they work on the FT-70.</p>

<p><img alt="Too tired to program with button" src="https://www.chunkofchange.com/wp-content/uploads/PC-load-letter-300x300.jpg" /></p>

<h2 id="adjusting-the-squelch">Adjusting the squelch</h2>

<ol>
<li>Smash the <code>F</code> button</li>
<li>Smash the <code>MON/T-CALL</code> button on the left side of the radio</li>
<li><code>SQL _</code> will appear on the screen</li>
<li>Use the dial to adjust the squelch up and down</li>
<li>Smash the <code>F</code> button to exit the squelch screen</li>
</ol>

<p><img alt="get rid of that noise" src="https://i.pinimg.com/736x/3d/bf/93/3dbf93d4efc023c8f49292804e34e833---dispatcher-paramedic-humor.jpg" /></p>

<h2 id="changing-the-transmission-power">Changing the transmission power</h2>

<ol>
<li>Smash the <code>F</code> button</li>
<li>Smash the <code>TX PO</code> button, also the <code>1</code> button.</li>
<li>You will see the transmission power screen appear</li>
<li>Use the dial to adjust the power up and down</li>
<li>Smash the <code>F</code> button to exit the power screen</li>
<li>On, what is known as, the <code>Home</code> screen you should see the power you selected under the channel name.</li>
</ol>

<p><img alt="You have all the power. Don&#39;t abuse it." src="https://media.tenor.com/images/efede05c9bd1b284dd7b97da0976d1f9/tenor.gif" /></p>

<h2 id="switching-to-the-manual-programming-mode-frequency-mode">Switching to the manual programming mode <code>frequency mode</code></h2>

<p>I call this <code>frequency</code> mode, because on my Baofeng, when I switch between programming modes, the nice robot lady voice says <code>frequency mode</code> or <code>channel mode</code>.</p>

<p>You don&#39;t have to use a pre-programmed channel. You can manually adjust the frequency, and other settings, without having to program a new channel.</p>

<h3 id="switching">Switching</h3>

<ol>
<li> Smash the <code>V\M</code> button</li>
<li>You should see the screen change to a frequency</li>
<li>Use the dial to adjust the frequency up and down or use the keypad to enter the frequency</li>
</ol>

<h3 id="manually-change-the-tone">Manually change the tone</h3>

<ol>
<li>Smash the <code>F</code> button</li>
<li>Smash the <code>code</code> or <code>6</code> button</li>
<li>Use the dial to adjust the tone code up or down</li>
<li>Smash the <code>F</code> button</li>
</ol>

<h2 id="other-notes">Other notes</h2>

<p>You need to smash that <code>F</code> button a lot. The keypad buttons are labelled and mostly make sense. Smashing the <code>F</code> button lets you use the function associated with the keypad button (labelled in orange).</p>

<p><img alt="mashing of buttons" src="https://static.tvtropes.org/pmwiki/pub/images/BRB.png" /></p>
]]></content:encoded>
      </item>
      <item>
        <guid>https://marnee.silvrback.com/f-for-aprs#47154</guid>
          <pubDate>Sun, 19 May 2019 12:26:05 -0700</pubDate>
        <link>https://marnee.silvrback.com/f-for-aprs</link>
        <title>F# for APRS</title>
        <description> A system for sending and receiving APRS messages integrated with DireWolf, and built on .NET Core in F#.</description>
        <content:encoded><![CDATA[<p><img title="FAPRS" alt="alt text" src="https://raw.githubusercontent.com/MarneeDear/FAPRS/master/logo.png" /></p>

<p><em>This is also my submission for the <a href="http://foundation.fsharp.org/applied_fsharp_challenge">Applied F# Challenge</a> - F# in your organization or domain category.</em></p>

<h2 id="applied-f-challenge">Applied F# Challenge</h2>

<h4 id="author">Author</h4>

<p>Marnee Dearman (KG7SIO)</p>

<h4 id="github-repository">Github Repository</h4>

<p><a href="https://github.com/MarneeDear/FAPRS">MarneeDear/FAPRS</a></p>

<h4 id="domain">Domain</h4>

<p>Amateur radio communications protocols, specifically the very popular packet radio protocol, APRS -- Automatic Packet Reporting System. </p>

<h4 id="purpose">Purpose</h4>

<ul>
<li>Demonstrate the power of functional data modeling in communications protocol applications in general, and APRS specifically</li>
<li>Provide a cross-platform, simple, easy to use application for sending and receiving APRS messages.</li>
<li>Provide an automated way to track race participants during amateur radio supported long-distance races (e.g, 24 Hours in the Old Pueblo, CV 50/50). </li>
<li>Provide a framework for developing custom APRS applications.</li>
<li>Expose more people to F#</li>
<li>Get more people involved in amateur radio</li>
</ul>

<h4 id="the-highlights">The Highlights</h4>

<p>The code uses a lot of functional programming techniques and F# libraries to get things done, but here are the highlights:</p>

<ul>
<li><a href="https://fsharpforfunandprofit.com/posts/designing-with-types-intro/">Designing with types</a>

<ul>
<li>Single case unions</li>
<li>Constrained strings</li>
<li>Making impossible states impossible</li>
</ul></li>
<li><a href="https://fsharpforfunandprofit.com/posts/convenience-active-patterns/">Active patterns</a> for string parsing and validation</li>
<li><a href="https://fsharpforfunandprofit.com/posts/recipe-part2/">Railway oriented programming</a></li>
<li><a href="http://fsprojects.github.io/Argu/">Argu</a> for command line parsing</li>
<li><a href="https://github.com/haf/expecto">Expecto</a> for testing</li>
<li><a href="https://saturnframework.org/">Saturn</a> for building the web app/web API/web service</li>
</ul>

<h2 id="amateur-radio-and-aprs">Amateur Radio and APRS</h2>

<h3 id="what-is-amateur-radio">What is amateur radio?</h3>

<p>What Wikipedia thinks it is:</p>

<blockquote>
<p>Amateur radio, also known as ham radio, describes the use of radio frequency spectrum for purposes of non-commercial exchange of messages, wireless experimentation, self-training, private recreation, radiosport, contesting, and emergency communication</p>
</blockquote>

<p>What the <a href="">Radio Society of Great Britain</a> thinks it is (great video introduction):</p>

<blockquote>
<p><a href="https://youtu.be/8x6x_6mDVlQ" title="Ham radio is awesome."><img alt="Packet Radio" src="https://img.youtube.com/vi/8x6x_6mDVlQ/0.jpg" /></a></p>
</blockquote>

<p><em>click the video to watch on You Tube</em></p>

<h3 id="what-is-aprs">What is APRS?</h3>

<p>What Wikipedia thinks it is:</p>

<blockquote>
<p><em>Automatic Packet Reporting System</em> is an amateur radio-based system for real time digital communications of information of immediate value in the local area. Data can include object Global Positioning System coordinates, weather station telemetry, text messages, announcements, queries, and other telemetry.</p>
</blockquote>

<p>This video is a nice demonstration of what you can do with APRS. The system demonstrated in the video is similar to my F# for APRS system design.</p>

<blockquote>
<p><a href="http://www.youtube.com/watch?v=FJEVWMuz6Xg" title="Kantronics Packet Radio Mail and BBS Operations"><img alt="Packet Radio" src="https://img.youtube.com/vi/FJEVWMuz6Xg/0.jpg" /></a></p>
</blockquote>

<p><em>click the video to watch on You Tube</em></p>

<h2 id="system-design">System design</h2>

<p>FAPRS will act like a <a href="http://www.nwclimate.org/aprs/digipeater/">APRS digipeater</a> that you can also command to transmit your own messages for certain purposes.</p>

<blockquote>
<p>Note: this is a work in progress and not all features have been developed. </p>
</blockquote>

<h3 id="motivation-purpose-and-use-cases">Motivation, purpose, and use cases</h3>

<p>Hams often volunteer for middle-of-nowhere long distances races. You know the kind? Where 20 persons run 50 miles through the desert for fun? The participants need trail support and we Hams are best-equipped to run the communications system. We setup stations with mobile radios at known locations along the trail, make sure we can communicate with each other, and setup a process by which we share participant status and location, so we can keep track of them and request medical support if needed. It&#39;s great fun, and many of these events wouldn&#39;t happen if it weren&#39;t for us Hams.</p>

<p>Most of the time we use voice to communicate. This works well enough, but I have worked enough of these races to know that it can be a challenge to keep track of all of the runners. The problem is this system requires a lot of coordination and sometimes we talk over each other or don&#39;t always hear the messages. The problems only increase as the number of participants increase. </p>

<blockquote>
<p>It doesn&#39;t scale well.</p>
</blockquote>

<p>I wanted to develop a system using packet radio that could automatically send and receive participant status reports.</p>

<h3 id="logical-design">Logical design</h3>

<h4 id="requirements">Requirements</h4>

<ul>
<li>Store sent and received messages and re-send periodically</li>
<li>A management interface for entering participant status reports and general informational messages</li>
<li>Prioritize emergency messages and new reports</li>
<li>De-prioritize older messages and expire messages after a certain period of time</li>
<li>See a list of messages that were sent and received</li>
<li>Be accessible over WiFi so a keyboard and monitor are not required</li>
</ul>

<blockquote>
<p>Note: this is a work in progress and not all features have been developed. </p>
</blockquote>

<h3 id="physical-design">Physical design</h3>

<p><img alt="Imgur" src="https://i.imgur.com/9w47hfD.png" /></p>

<p>I designed this system with a few things in mind:</p>

<ul>
<li>It will be used in remote locations</li>
<li>Low-cost </li>
<li>Highly mobile -- can carry all of the parts in a backpack easily</li>
<li>Compatible with common hand-held VHF radios like my Baofeng UV-82* </li>
<li>Low-power and possible to run off a portable solar cell</li>
</ul>

<p>The Raspberry Pi 3 will provide these services</p>

<ul>
<li>WiFi hot-spot</li>
<li>DireWolf -- the Terminal Node Controller 

<ul>
<li>Handles encoding and decoding packets and sending them out the audio port to the radio</li>
</ul></li>
<li>FAPRS

<ul>
<li>Self-hosted web service providing

<ul>
<li>Management interface</li>
<li>Message scheduler and processor</li>
</ul></li>
<li>CLI

<ul>
<li>Can be used to manually craft APRS messages for testing and troubleshooting</li>
</ul></li>
<li>Database (<code>SQLite</code>) for storing sent and received messages</li>
</ul></li>
<li>Wired to a VHF radio through the audio port

<ul>
<li>A USB sound card may be used</li>
<li>Need both an audio-in and audio-out (mic and speaker) so that messages can be both received and transmitted.</li>
</ul></li>
</ul>

<h3 id="my-equipment">My equipment</h3>

<ul>
<li><a href="https://www.raspberrypi.org/products/raspberry-pi-3-model-b/">RaspberryPi 3</a></li>
<li><a href="https://baofengtech.com/uv82">Baofeng UV-82</a></li>
<li><a href="https://baofengtech.com/aprs-k2-trrs-cable">BTech APRS K2 Cable (connect radio to RaspberryPi or laptop via the audio port)</a></li>
<li>My laptop</li>
<li>My mobile phone (Android)</li>
</ul>

<h3 id="direwolf-integration">DireWolf integration</h3>

<p>The DireWolf integration ended up being really simple because the developer provided a KISS utility that reads TNC2MON formatted messages from a file and converts them to KISS format to be transmitted via the TNC (in this case DireWolf is also running as a TNC).</p>

<h4 id="kissutil">kissutil</h4>

<p>The DireWolf <code>kissutil</code> makes it easy to send and receive messages. The <code>kissutil</code> reads TNC2MON formated messages from files in a folder you specify and then sends them to the DireWolf TNC for sending out over the radio. The <code>kissutil</code> also writes received packets to a folder you specify. The Direwolf integration reads from, and writes to, these folders.</p>

<p>The records are in the TNC2MON format, which I will talk about more later.</p>

<h3 id="application-architecture">Application architecture</h3>

<p>The overall pattern is following Onion Architecture/Clean Architecture. It consists of:</p>

<ul>
<li>A core library where the data models live (<code>faprs.core</code>)</li>
<li>An infrastructure library where data operations and business logic happens (<code>faprs.infrastructure</code>)</li>
<li>A CLI for creating messages for the <code>kissutil</code> (<code>faprs.cli</code>)

<ul>
<li>The CLI uses <a href="http://fsprojects.github.io/Argu/">Argu</a></li>
</ul></li>
<li>A web service that hosts a web interface for creating and displaying messages to send and receive through the <code>kissutil</code>(<code>faprs.service</code>)

<ul>
<li>The web service uses <a href="https://saturnframework.org/">Saturn</a></li>
</ul></li>
<li>A SQL Lite database (database.sqlite) for storing sent and received messages.</li>
<li>Tests (<code>faprs.tests</code>)</li>
<li>Database migrations (<code>faprs.migrations</code>)</li>
</ul>

<h2 id="aprs-specification-and-implementation">APRS specification and implementation</h2>

<p>The APRS protocol was developed by Bob Bruninga, WB4APR.</p>

<p>There are 3 specification versions.</p>

<ul>
<li><a href="http://www.aprs.org/doc/APRS101.PDF">APRS v1.0</a></li>
<li><a href="http://www.aprs.org/aprs11.html">APRS v1.1</a></li>
<li><a href="http://www.aprs.org/aprs12.html">APRS v1.2</a></li>
</ul>

<h3 id="tnc-2-monitor-format-tnc2mon">TNC 2 Monitor format (TNC2MON)</h3>

<p>The <code>kissutil</code> accepts APRS packets in the TNC 2 Monitor format. This format is defined in the<br>
APRS version 1.0.1 specification under the section <code>Network Tunneling and Third-Party Digipeating.</code> </p>

<p>FAPRS takes message details and outputs a <code>TNC2MON</code> formatted packet.</p>

<p>It looks like this:</p>

<blockquote>
<p><em>SENDER</em>&gt;<em>DESTINATION</em>,<em>PATH</em>:<em>MESSAGE</em></p>
</blockquote>

<p>The packet consists of a source, a destination, a path, and a message. The message can be user-defined, but is most often a Position Report or a Weather Report. There are a number of formats defined by the APRS spec. </p>

<h4 id="sender">SENDER</h4>

<p>The <code>SENDER</code> is always the transmitting station&#39;s call sign. </p>

<p>For example, my station&#39;s call sign is <code>KG7SIO</code>, because that was the call sign assigned to me by the FCC when I got my license.</p>

<p>Since sender is a call sign, I created a CallSign type (in the <code>Common</code> namespace in <code>faprs.core</code>). I used a <code>single case union type</code> as described by Scott Wlashcin <a href="https://fsharpforfunandprofit.com/posts/designing-with-types-single-case-dus/">here</a>.</p>
<div class="highlight"><pre><span></span><span class="c1">//9 byte field</span>
<span class="k">type</span> <span class="nc">CallSign</span> <span class="o">=</span> <span class="k">private</span> <span class="n">CallSign</span> <span class="k">of</span> <span class="kt">string</span>          
<span class="k">module</span> <span class="nn">CallSign</span> <span class="o">=</span>
    <span class="k">open</span> <span class="nn">System</span>

    <span class="k">let</span> <span class="nv">create</span> <span class="o">(</span><span class="n">s</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span> 
        <span class="k">match</span> <span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="n">Trim</span><span class="bp">()</span><span class="o">)</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">c</span> <span class="k">when</span> <span class="ow">not</span> <span class="o">(</span><span class="nn">String</span><span class="p">.</span><span class="n">IsNullOrEmpty</span><span class="o">(</span><span class="n">c</span><span class="o">))</span> <span class="o">&amp;&amp;</span> <span class="n">c</span><span class="o">.</span><span class="n">Length</span> <span class="o">&lt;</span> <span class="mi">10</span>     <span class="o">-&gt;</span> <span class="n">Some</span> <span class="o">(</span><span class="n">CallSign</span> <span class="n">c</span><span class="o">)</span>
        <span class="o">|</span> <span class="o">_</span>                                                         <span class="o">-&gt;</span> <span class="n">None</span> <span class="c1">// &quot;Call Sign cannot be empty and must be 1 - 9 characters. See APRS 1.01.&quot;</span>
    <span class="k">let</span> <span class="nv">value</span> <span class="o">(</span><span class="n">CallSign</span> <span class="n">s</span><span class="o">)</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">ToUpper</span><span class="bp">()</span> <span class="c1">// MUST BE ALL CAPS        </span>
</pre></div>
<p>This makes it so that anywhere I need a CallSign I can be guaranteed to have a properly formated call sign.</p>

<h4 id="destination">DESTINATION</h4>

<p>The <code>DESTINATION</code> can be the call sign of a particular station for which a message is intended, but <code>DESTINATION</code> is overloaded and can be used to pass on other encoded bits of information. One common usage is to identify the sending application and version number. You can see a list of TAPR approved to-calls <a href="http://aprs.org/aprs11/tocalls.txt">here</a>.</p>

<p>By default, the <code>fapr.cli</code> will uses the DireWolf TOCALL, <code>APDW15</code>.</p>

<p>Since destination is also a call sign, I can use the CallSign type.</p>

<h4 id="path">PATH</h4>

<p>The <code>PATH</code> is also known as the digipath, and specifies if and how an APRS package should be repeated (re-transmitted) when received by a digital repeater (digipeater). This is intended to avoid repeating packets redundantly, and reduce the amount of traffic on the APRS network. The digipeater will be configured to re-transmit according to the PATH, depending on its location and general network conditions, in order to help prevent network congestion. </p>

<blockquote>
<p>PATH settings determine what kind and how many digipeaters will be used to deliver your packets to their destination.</p>
</blockquote>

<p>For example, <code>WIDE1-1</code>:</p>

<blockquote>
<p>It requests that a &quot;wide&quot; digipeater (one with a wide coverage area, like on a mountaintop) repeat the packet, but only once; if a second &quot;wide&quot; digipeater should hear the rebroadcast packet, then the second digipeater wouldn&#39;t repeat it.</p>
</blockquote>

<p>The PATH part is best defined and explained <a href="http://wa8lmf.net/DigiPaths/">here</a> and <a href="https://ham.stackexchange.com/questions/6213/help-understanding-path-taken-by-aprs-packet?rq=1">here</a>.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">WIDEnN</span> <span class="o">=</span>
    <span class="o">|</span> <span class="n">WIDE11</span>
    <span class="o">|</span> <span class="n">WIDE21</span>
    <span class="o">|</span> <span class="n">WIDE22</span>
    <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToString</span><span class="bp">()</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">this</span> <span class="k">with</span> 
        <span class="o">|</span> <span class="n">WIDE11</span>    <span class="o">-&gt;</span> <span class="s">&quot;WIDE1-1&quot;</span>
        <span class="o">|</span> <span class="n">WIDE21</span>    <span class="o">-&gt;</span> <span class="s">&quot;WIDE2-1&quot;</span>
        <span class="o">|</span> <span class="n">WIDE22</span>    <span class="o">-&gt;</span> <span class="s">&quot;WIDE2-2&quot;</span>
    <span class="k">static</span> <span class="k">member</span> <span class="n">fromString</span> <span class="n">p</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">p</span> <span class="k">with</span>
        <span class="o">|</span> <span class="s">&quot;WIDE1-1&quot;</span> <span class="o">-&gt;</span> <span class="n">WIDE11</span>
        <span class="o">|</span> <span class="s">&quot;WIDE2-1&quot;</span> <span class="o">-&gt;</span> <span class="n">WIDE21</span>
        <span class="o">|</span> <span class="s">&quot;WIDE2-2&quot;</span> <span class="o">-&gt;</span> <span class="n">WIDE22</span>
        <span class="o">|</span> <span class="o">_</span>         <span class="o">-&gt;</span> <span class="n">WIDE11</span> <span class="c1">//Use this as the default</span>

<span class="c1">//9 byte field</span>
<span class="c1">//aka the UNPROTO path</span>
<span class="c1">//http://wa8lmf.net/DigiPaths/index.htm#Recommended</span>
<span class="k">type</span> <span class="nc">Path</span> <span class="o">=</span>
    <span class="o">|</span> <span class="n">WIDEnN</span> <span class="k">of</span> <span class="n">WIDEnN</span>
    <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToString</span><span class="bp">()</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">WIDEnN</span> <span class="n">p</span> <span class="o">-&gt;</span>   <span class="k">match</span> <span class="n">p</span> <span class="k">with</span>
                        <span class="o">|</span> <span class="n">WIDE11</span>    <span class="o">-&gt;</span> <span class="nn">WIDE11</span><span class="p">.</span><span class="n">ToString</span><span class="bp">()</span>
                        <span class="o">|</span> <span class="n">WIDE21</span>    <span class="o">-&gt;</span> <span class="nn">WIDE21</span><span class="p">.</span><span class="n">ToString</span><span class="bp">()</span>
                        <span class="o">|</span> <span class="n">WIDE22</span>    <span class="o">-&gt;</span> <span class="nn">WIDE22</span><span class="p">.</span><span class="n">ToString</span><span class="bp">()</span>
</pre></div>
<p>The PATH part used to have a number of types, but those were made obsolete and the recommend PATH is to only use one of the <code>WIDEnN</code> options. That is how I modeled it here.</p>

<h4 id="message">MESSAGE</h4>

<p>The <code>MESSAGE</code> is also known as the <code>payload</code>. This is the data you want to transmit <code>and the fun part</code>.</p>

<p>The <code>MESSAGE</code> is also where it starts to get more complicated. As <code>APRS</code> started as a position reporting system, APRS specifies a number of standard message formats for identifying a station&#39;s position, but also provides for user-defined messages, weather reports, telemetry, and plain old messages (as if you were tweeting). </p>

<p>FAPRS supports a number of message formats. I will cover some of them, here.</p>

<h3 id="aprs-data-formats">APRS data formats</h3>

<p>All of the supported message formats are defined by a union type. Each of the options has its own type.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">Message</span> <span class="o">=</span>
    <span class="o">|</span> <span class="n">Unformatted</span>                       <span class="k">of</span> <span class="n">UnformattedMessage</span>
    <span class="o">|</span> <span class="n">PositionReportWithoutTimeStamp</span>    <span class="k">of</span> <span class="n">PositionReportWithoutTimeStamp</span>
    <span class="o">|</span> <span class="n">ParticipantStatusReport</span>           <span class="k">of</span> <span class="nn">Participant</span><span class="p">.</span><span class="n">ParticipantStatusReport</span>
    <span class="o">|</span> <span class="n">Unsupported</span>                       <span class="k">of</span> <span class="n">UnformattedMessage</span>
    <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToString</span><span class="bp">()</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">this</span> <span class="k">with</span> 
        <span class="o">|</span> <span class="n">Unformatted</span> <span class="n">m</span>                     <span class="o">-&gt;</span> <span class="nn">UnformattedMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span>
        <span class="o">|</span> <span class="n">PositionReportWithoutTimeStamp</span> <span class="n">r</span>  <span class="o">-&gt;</span> <span class="n">r</span><span class="o">.</span><span class="n">ToString</span><span class="bp">()</span>
        <span class="o">|</span> <span class="n">ParticipantStatusReport</span> <span class="n">r</span>         <span class="o">-&gt;</span> <span class="n">r</span><span class="o">.</span><span class="n">ToString</span><span class="bp">()</span>
        <span class="o">|</span> <span class="n">Unsupported</span> <span class="n">u</span>                     <span class="o">-&gt;</span> <span class="nn">UnformattedMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">u</span> <span class="c1">//This is where anything that cant be parsed will end up</span>
</pre></div>
<h4 id="unformatted-message">Unformatted Message</h4>

<p>An unformatted message must start with <code>:</code>, and has a size constraint, but otherwise can contain anything. I used a single case union type for this one, too.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">UnformattedMessage</span> <span class="o">=</span> <span class="k">private</span> <span class="n">UnformattedMessage</span> <span class="k">of</span> <span class="kt">string</span>
<span class="k">module</span> <span class="nn">UnformattedMessage</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">create</span> <span class="o">(</span><span class="n">m</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
        <span class="k">match</span> <span class="o">(</span><span class="n">m</span><span class="o">.</span><span class="n">Trim</span><span class="bp">()</span><span class="o">)</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">m</span> <span class="k">when</span> <span class="n">m</span><span class="o">.</span><span class="n">Length</span> <span class="o">&lt;=</span> <span class="mi">255</span>    <span class="o">-&gt;</span> <span class="n">UnformattedMessage</span> <span class="n">m</span> <span class="c1">//AX.25 field is 256 chars but the message has to accommodate the { for user defined messages</span>
        <span class="o">|</span> <span class="o">_</span>                         <span class="o">-&gt;</span> <span class="n">UnformattedMessage</span> <span class="o">(</span><span class="n">m</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">255</span><span class="o">))</span> 
    <span class="k">let</span> <span class="nv">value</span> <span class="o">(</span><span class="n">UnformattedMessage</span> <span class="n">m</span><span class="o">)</span> <span class="o">=</span> <span class="n">sprintf</span> <span class="s">&quot;:%s&quot;</span> <span class="n">m</span>
</pre></div>
<h4 id="lat-long-position-report-format-without-timestamp">Lat/Long Position Report Format — without Timestamp</h4>

<p>This format is defined in <code>APRS 1.01</code> <code>6 TIME AND POSITION FORMATS</code> and <code>8 POSITION AND DF REPORT DATA FORMATS</code></p>

<p>It looks like this:</p>
<div class="highlight"><pre><span></span>        ! or |
        =    | Latitude |  /  | Longitude | Symbol | Comment (max 43 chars)
Bytes    1         8       1        9          1           0-43
</pre></div>
<p>Example:</p>
<div class="highlight"><pre><span></span>!4903.50N/07201.75W-Test
</pre></div>
<p>The latitude and longitude are expected to be in the APRS format defined in <code>6 TIME AND POSITION FORMATS</code>.</p>

<p>I created a PositionReportWithoutTimeStamp record type that includes the fields of the position report.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">PositionReportWithoutTimeStamp</span> <span class="o">=</span>
    <span class="o">{</span>
        <span class="n">Position</span>    <span class="o">:</span> <span class="n">Position</span>
        <span class="n">Symbol</span>      <span class="o">:</span> <span class="n">SymbolCode</span>
        <span class="n">Comment</span>     <span class="o">:</span> <span class="n">PositionReportComment</span>
    <span class="o">}</span>
    <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToString</span><span class="bp">()</span> <span class="o">=</span>
        <span class="n">sprintf</span> <span class="s">&quot;=%s/%s%c%s&quot;</span> <span class="o">(</span><span class="nn">FormattedLatitude</span><span class="p">.</span><span class="n">value</span> <span class="n">this</span><span class="o">.</span><span class="n">Position</span><span class="o">.</span><span class="n">Latitude</span><span class="o">)</span> <span class="o">(</span><span class="nn">FormattedLongitude</span><span class="p">.</span><span class="n">value</span> <span class="n">this</span><span class="o">.</span><span class="n">Position</span><span class="o">.</span><span class="n">Longitude</span><span class="o">)</span> <span class="o">(</span><span class="n">this</span><span class="o">.</span><span class="n">Symbol</span><span class="o">.</span><span class="n">ToChar</span><span class="bp">()</span><span class="o">)</span> <span class="o">(</span><span class="nn">PositionReportComment</span><span class="p">.</span><span class="n">value</span> <span class="n">this</span><span class="o">.</span><span class="n">Comment</span><span class="o">)</span>  
</pre></div>
<p><code>Position</code> is the latitude and longitude in APRS format.</p>

<p>The <code>PositionReportWithoutTimeStamp</code> will also return the string representation of a position report.</p>

<h5 id="latitude-and-longitude">Latitude and longitude</h5>

<p>Latitude and longitude take a decimal coordinate and convert it to the <code>APRS format</code>.</p>

<p>From the APRS spec:</p>
<div class="highlight"><pre><span></span>Latitude is expressed as a fixed 8-character field, in degrees and decimal minutes (to two decimal places), followed by the letter `N` for north or `S` for south.
</pre></div><div class="highlight"><pre><span></span>Longitude is expressed as a fixed 9-character field, in degrees and decimal minutes (to two decimal places), followed by the letter `E` for east or `W` for west.
</pre></div>
<p>I created two single case union types called <code>FormattedLatitude</code> and <code>FormattedLongitude</code> that do the conversion and create a formatted latitude or longitude. The hemisphere designation is further constrained by a type.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">LatitudeHemisphere</span> <span class="o">=</span>
    <span class="o">|</span> <span class="n">North</span>     
    <span class="o">|</span> <span class="n">South</span>     
    <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToHemisphereChar</span><span class="bp">()</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">North</span> <span class="o">_</span>   <span class="o">-&gt;</span> <span class="sc">&#39;N&#39;</span>
        <span class="o">|</span> <span class="n">South</span> <span class="o">_</span>   <span class="o">-&gt;</span> <span class="sc">&#39;S&#39;</span>
    <span class="k">static</span> <span class="k">member</span> <span class="n">fromHemisphere</span> <span class="n">h</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">h</span> <span class="k">with</span>
        <span class="o">|</span> <span class="sc">&#39;N&#39;</span>   <span class="o">-&gt;</span> <span class="n">Some</span> <span class="nn">LatitudeHemisphere</span><span class="p">.</span><span class="n">North</span>
        <span class="o">|</span> <span class="sc">&#39;S&#39;</span>   <span class="o">-&gt;</span> <span class="n">Some</span> <span class="nn">LatitudeHemisphere</span><span class="p">.</span><span class="n">South</span>
        <span class="o">|</span> <span class="o">_</span>     <span class="o">-&gt;</span> <span class="n">None</span> <span class="c1">//&quot;Latitude must be in northern (N) or southern (S) hemisphere.&quot;</span>

<span class="k">type</span> <span class="nc">FormattedLatitude</span> <span class="o">=</span> <span class="k">private</span> <span class="n">FormattedLatitude</span> <span class="k">of</span> <span class="kt">string</span>
<span class="k">module</span> <span class="nn">FormattedLatitude</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">create</span> <span class="o">(</span><span class="n">d</span><span class="o">:</span><span class="kt">float</span><span class="o">)</span> <span class="o">=</span>
        <span class="k">let</span> <span class="nv">deg</span><span class="o">,</span> <span class="n">min</span><span class="o">,</span> <span class="n">sec</span> <span class="o">=</span> <span class="n">calcDegMinSec</span> <span class="n">d</span>
        <span class="n">FormattedLatitude</span> <span class="o">(</span><span class="n">sprintf</span> <span class="s">&quot;%02i%02i.%02i%c&quot;</span> <span class="n">deg</span> <span class="n">min</span> <span class="n">sec</span> <span class="o">(</span><span class="k">if</span> <span class="n">d</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">.</span><span class="mi">0</span> <span class="k">then</span> <span class="o">(</span><span class="nn">North</span><span class="p">.</span><span class="n">ToHemisphereChar</span><span class="bp">()</span><span class="o">)</span> <span class="k">else</span> <span class="o">(</span><span class="nn">South</span><span class="p">.</span><span class="n">ToHemisphereChar</span><span class="bp">()</span><span class="o">)))</span>
    <span class="k">let</span> <span class="nv">check</span> <span class="o">(</span><span class="n">d</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
        <span class="n">FormattedLatitude</span> <span class="n">d</span> 
    <span class="k">let</span> <span class="nv">value</span> <span class="o">(</span><span class="n">FormattedLatitude</span> <span class="n">d</span><span class="o">)</span> <span class="o">=</span> <span class="n">d</span>
</pre></div>
<h5 id="symbol">Symbol</h5>

<p>APRS defines a list of symbols that can be rendered on a map and represent a station. There are a number of symbols defined, but FAPRS only supports some of them.</p>
<div class="highlight"><pre><span></span>    <span class="c1">//This is only a subset of the codes because I don&#39;t want to support all of them at this time</span>
<span class="k">type</span> <span class="nc">SymbolCode</span> <span class="o">=</span>
    <span class="o">|</span> <span class="n">House</span> 
    <span class="o">|</span> <span class="n">Bicycle</span> 
    <span class="o">|</span> <span class="n">Balloon</span> 
    <span class="o">|</span> <span class="n">Hospital</span>
    <span class="o">|</span> <span class="n">Jeep</span> 
    <span class="o">|</span> <span class="n">Truck</span>
    <span class="o">|</span> <span class="n">Motorcycle</span>
    <span class="o">|</span> <span class="n">Jogger</span>
    <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToChar</span><span class="bp">()</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">House</span>         <span class="o">-&gt;</span> <span class="sc">&#39;-&#39;</span>
        <span class="o">|</span> <span class="n">Bicycle</span>       <span class="o">-&gt;</span> <span class="sc">&#39;b&#39;</span>
        <span class="o">|</span> <span class="n">Balloon</span>       <span class="o">-&gt;</span> <span class="sc">&#39;O&#39;</span>
        <span class="o">|</span> <span class="n">Hospital</span>      <span class="o">-&gt;</span> <span class="sc">&#39;h&#39;</span>
        <span class="o">|</span> <span class="n">Jeep</span>          <span class="o">-&gt;</span> <span class="sc">&#39;j&#39;</span>
        <span class="o">|</span> <span class="n">Truck</span>         <span class="o">-&gt;</span> <span class="sc">&#39;k&#39;</span>
        <span class="o">|</span> <span class="n">Motorcycle</span>    <span class="o">-&gt;</span> <span class="sc">&#39;&lt;&#39;</span>
        <span class="o">|</span> <span class="n">Jogger</span>        <span class="o">-&gt;</span> <span class="sc">&#39;[&#39;</span>
    <span class="k">static</span> <span class="k">member</span> <span class="n">fromSymbol</span> <span class="n">s</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">s</span> <span class="k">with</span>
        <span class="o">|</span> <span class="sc">&#39;-&#39;</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">House</span>
        <span class="o">|</span> <span class="sc">&#39;b&#39;</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">Bicycle</span>
        <span class="o">|</span> <span class="sc">&#39;O&#39;</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">Balloon</span>
        <span class="o">|</span> <span class="sc">&#39;h&#39;</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">Hospital</span>
        <span class="o">|</span> <span class="sc">&#39;j&#39;</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">Jeep</span>
        <span class="o">|</span> <span class="sc">&#39;k&#39;</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">Truck</span>
        <span class="o">|</span> <span class="sc">&#39;&lt;&#39;</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">Motorcycle</span>
        <span class="o">|</span> <span class="sc">&#39;[&#39;</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">Jogger</span>
        <span class="o">|</span> <span class="o">_</span>   <span class="o">-&gt;</span> <span class="n">None</span>
</pre></div>
<h5 id="comment">Comment</h5>

<p>The <code>COMMENT</code> field has a size constraint, but otherwise can contain anything. I used a single case union type for this one, too.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">PositionReportComment</span> <span class="o">=</span> <span class="k">private</span> <span class="n">PositionReportComment</span> <span class="k">of</span> <span class="kt">string</span>
<span class="k">module</span> <span class="nn">PositionReportComment</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">create</span> <span class="o">(</span><span class="n">s</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
        <span class="k">match</span> <span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="n">Trim</span><span class="bp">()</span><span class="o">)</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">s</span> <span class="k">when</span> <span class="n">s</span><span class="o">.</span><span class="n">Length</span> <span class="o">&lt;</span> <span class="mi">44</span>  <span class="o">-&gt;</span> <span class="n">Some</span> <span class="o">(</span><span class="n">PositionReportComment</span> <span class="n">s</span><span class="o">)</span>
        <span class="o">|</span> <span class="o">_</span>                     <span class="o">-&gt;</span> <span class="n">None</span> 

    <span class="k">let</span> <span class="nv">value</span> <span class="o">(</span><span class="n">PositionReportComment</span> <span class="n">c</span><span class="o">)</span> <span class="o">=</span> <span class="n">c</span> <span class="c1">//Was trimmed during create</span>
</pre></div>
<h4 id="participant-status-report">Participant Status Report</h4>

<p>The <code>Participant Status Report</code> is a user-defined format that I created in order to facilitate tracking race participants. </p>

<p>User-defined formating is defined in APRS 1.01 <code>18 USER DEFINED FORMATS</code>. The first 3 characters of a user-defined data format are the data identifiers. </p>

<ul>
<li>{ APRS Data Type Identifier.</li>
<li>U A one-character User ID.</li>
<li>X A one-character user-defined packet type.</li>
</ul>

<p>The APRS spec makes allowances for experimental user-defined formats. Experimental formats must start with <code>{{</code>. I chose <code>P</code> as the experimental user-defined data identifier. This makes the full identifier <code>{{P</code>.</p>

<p>The APRS data needs to fit inside the <a href="https://en.wikipedia.org/wiki/AX.25">AX.25 information field</a>, which is defined as 1-256 bytes. </p>

<p>Participant Status should include these parts:<br>
* User-defined data type<br>
* 253 chars max<br>
* Participant number (bib number)<br>
* Time last seen at comm station<br>
* Status (continued, injured, waiting for help, taking a break) </p>
<div class="highlight"><pre><span></span>Participant Status Field
        TIMESTAMP   PARTICIPANT-ID      STATUS-1    STATUS-2    MESSAGE
BYTES   8-fixed     5-fixed             1-fixed     1-fixed     0-238 
</pre></div>
<p>Example including the data identifier:</p>
<div class="highlight"><pre><span></span>{{P100923450004211In good shape!
</pre></div><div class="highlight"><pre><span></span>IDENTIFIER  TIMESTAMP   PARTICIPANT-ID  STATUS-1    STATUS-2    MESSAGE
{{P         10092345    00042           1           1           In good shape!
            2019-10-09 23:34            Continued   Continued
</pre></div>
<p>The <code>TIMESTAMP</code> field is an APRS formatted timestamp.</p>
<div class="highlight"><pre><span></span>TIMESTAMP
Month/Day/Hours/Minutes (MDHM) format is a fixed 8-character field, consisting of the month (01–12) and day-of-the-month (01–31), followed by the time in hours and minutes zulu. For example: 10092345 is 23 hours 45 minutes zulu on October 9th.
</pre></div>
<p>I created a new record type called <code>ParticipantStatusReport</code> that defines 3 fields.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">ParticipantStatusReport</span> <span class="o">=</span>
    <span class="o">{</span>
        <span class="n">TimeStamp</span>           <span class="o">:</span> <span class="n">RecordedOn</span>
        <span class="n">ParticipantID</span>       <span class="o">:</span> <span class="n">ParticipantID</span>
        <span class="n">ParticipantStatus</span>   <span class="o">:</span> <span class="n">ParticipantStatus</span>
    <span class="o">}</span>
    <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToString</span><span class="bp">()</span> <span class="o">=</span>
        <span class="k">let</span> <span class="o">(</span><span class="n">status1</span><span class="o">,</span> <span class="n">status2</span><span class="o">,</span> <span class="n">msg</span><span class="o">)</span> <span class="o">=</span> <span class="n">this</span><span class="o">.</span><span class="n">ParticipantStatus</span><span class="o">.</span><span class="n">ToStatusCombination</span><span class="bp">()</span>
        <span class="n">sprintf</span> <span class="s">&quot;{{P%s%s%i%i%s%s&quot;</span> <span class="o">(</span><span class="nn">RecordedOn</span><span class="p">.</span><span class="n">value</span> <span class="n">this</span><span class="o">.</span><span class="n">TimeStamp</span><span class="o">)</span> <span class="o">(</span><span class="nn">ParticipantID</span><span class="p">.</span><span class="n">value</span> <span class="n">this</span><span class="o">.</span><span class="n">ParticipantID</span><span class="o">)</span> <span class="n">status1</span> <span class="n">status2</span> <span class="n">msg</span>
</pre></div>
<p><code>RecordedOn</code> is a single case union type that converts a DateTime value to an APRS formatted timestamp. It creates a timestamp, and it will revert a timestamp back to a DateTime.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">RecordedOn</span> <span class="o">=</span> <span class="k">private</span> <span class="n">RecordedOn</span> <span class="k">of</span> <span class="kt">string</span>
<span class="k">module</span> <span class="nn">RecordedOn</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">revert</span> <span class="o">(</span><span class="n">timestamp</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span> <span class="c1">//TODO would this be better in an active pattern?</span>
        <span class="k">let</span> <span class="nv">mm</span> <span class="o">=</span> <span class="o">(</span><span class="n">timestamp</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">2</span><span class="o">))</span>
        <span class="k">let</span> <span class="nv">dd</span> <span class="o">=</span> <span class="o">(</span><span class="n">timestamp</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">2</span><span class="o">))</span>
        <span class="k">let</span> <span class="nv">HH</span> <span class="o">=</span> <span class="o">(</span><span class="n">timestamp</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">4</span><span class="o">,</span> <span class="mi">2</span><span class="o">))</span>
        <span class="k">let</span> <span class="nv">MM</span> <span class="o">=</span> <span class="o">(</span><span class="n">timestamp</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">6</span><span class="o">,</span> <span class="mi">2</span><span class="o">))</span>
        <span class="k">let</span> <span class="nv">dt</span> <span class="o">=</span> <span class="n">sprintf</span> <span class="s">&quot;%i-%s-%sT%s:%s&quot;</span> <span class="nn">DateTime</span><span class="p">.</span><span class="nn">Today</span><span class="p">.</span><span class="n">Year</span> <span class="n">mm</span> <span class="n">dd</span> <span class="n">HH</span> <span class="n">MM</span>
        <span class="nn">DateTime</span><span class="p">.</span><span class="n">Parse</span><span class="o">(</span><span class="n">dt</span><span class="o">)</span>
    <span class="k">let</span> <span class="nv">create</span> <span class="o">(</span><span class="n">date</span><span class="o">:</span><span class="n">DateTime</span> <span class="n">option</span><span class="o">)</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">date</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">Some</span> <span class="n">d</span>    <span class="o">-&gt;</span> <span class="n">RecordedOn</span> <span class="o">(</span><span class="n">sprintf</span> <span class="s">&quot;%02i%02i%02i%02i&quot;</span> <span class="n">d</span><span class="o">.</span><span class="n">Month</span> <span class="n">d</span><span class="o">.</span><span class="n">Day</span> <span class="n">d</span><span class="o">.</span><span class="n">Hour</span> <span class="n">d</span><span class="o">.</span><span class="n">Minute</span><span class="o">)</span>
        <span class="o">|</span> <span class="n">None</span>      <span class="o">-&gt;</span> <span class="k">let</span> <span class="nv">utcNow</span> <span class="o">=</span> <span class="nn">DateTime</span><span class="p">.</span><span class="n">Now</span>
                        <span class="n">RecordedOn</span> <span class="o">(</span><span class="n">sprintf</span> <span class="s">&quot;%02i%02i%02i%02i&quot;</span> <span class="n">utcNow</span><span class="o">.</span><span class="n">Month</span> <span class="n">utcNow</span><span class="o">.</span><span class="n">Day</span> <span class="n">utcNow</span><span class="o">.</span><span class="n">Hour</span> <span class="n">utcNow</span><span class="o">.</span><span class="n">Minute</span><span class="o">)</span>
    <span class="k">let</span> <span class="nv">value</span> <span class="o">(</span><span class="n">RecordedOn</span> <span class="n">d</span><span class="o">)</span> <span class="o">=</span> <span class="n">d</span>
</pre></div>
<p><code>ParticipantID</code> is fixed-width, can be any characters, and is 0 padded to fill the empty space.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">ParticipantID</span> <span class="o">=</span> <span class="k">private</span> <span class="n">ParticipantID</span> <span class="k">of</span> <span class="kt">string</span>
<span class="k">module</span> <span class="nn">ParticipantID</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">create</span> <span class="o">(</span><span class="n">nbr</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">nbr</span> <span class="k">with</span> 
        <span class="o">|</span> <span class="n">n</span> <span class="k">when</span> <span class="nn">String</span><span class="p">.</span><span class="n">IsNullOrWhiteSpace</span><span class="o">(</span><span class="n">n</span><span class="o">)</span>   <span class="o">-&gt;</span> <span class="n">None</span>
        <span class="o">|</span> <span class="n">n</span> <span class="k">when</span> <span class="n">nbr</span><span class="o">.</span><span class="n">Length</span> <span class="o">&lt;</span> <span class="mi">6</span>                 <span class="o">-&gt;</span> <span class="n">Some</span> <span class="o">(</span><span class="n">ParticipantID</span> <span class="o">(</span><span class="n">sprintf</span> <span class="s">&quot;%5s&quot;</span> <span class="n">n</span><span class="o">))</span> <span class="c1">//Fixed width 5 chars</span>
        <span class="o">|</span> <span class="o">_</span>                                     <span class="o">-&gt;</span> <span class="n">None</span> 
    <span class="k">let</span> <span class="nv">value</span> <span class="o">(</span><span class="n">ParticipantID</span> <span class="n">n</span><span class="o">)</span> <span class="o">=</span> <span class="n">n</span>
</pre></div>
<p><code>ParticipantStatus</code> is fixed-width and limited to a set of statuses in a combination of 1 or 2 status options, plus a free form message. I modeled this as a tuple of (status, status, message).</p>

<p>Only <code>Injured</code> has a sub status combination. For example, an <code>Injured</code> participant could also be <code>Continued</code>, <code>Resting</code>, <code>NeedsEmergencySupport</code>, <code>DroppedOut</code>, <code>Unknown</code>.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">ParticipantStatusMessage</span> <span class="o">=</span> <span class="k">private</span> <span class="n">ParticipantStatusMessage</span> <span class="k">of</span> <span class="kt">string</span>
<span class="k">module</span> <span class="nn">ParticipantStatusMessage</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">create</span> <span class="o">(</span><span class="n">s</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
        <span class="k">match</span> <span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="n">Trim</span><span class="bp">()</span><span class="o">)</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">s</span> <span class="k">when</span> <span class="n">s</span><span class="o">.</span><span class="n">Length</span> <span class="o">&lt;=</span> <span class="mi">239</span> <span class="o">-&gt;</span> <span class="n">ParticipantStatusMessage</span> <span class="n">s</span>
        <span class="o">|</span> <span class="o">_</span> <span class="o">-&gt;</span> <span class="n">ParticipantStatusMessage</span> <span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">239</span><span class="o">))</span>
    <span class="k">let</span> <span class="nv">value</span> <span class="o">(</span><span class="n">ParticipantStatusMessage</span> <span class="n">s</span><span class="o">)</span> <span class="o">=</span> <span class="n">s</span>

<span class="k">type</span> <span class="nc">ParticipantStatus</span> <span class="o">=</span>
    <span class="o">|</span> <span class="n">Continued</span>                 <span class="k">of</span> <span class="n">ParticipantStatusMessage</span>
    <span class="o">|</span> <span class="n">Injured</span>                   <span class="k">of</span> <span class="n">ParticipantStatus</span>
    <span class="o">|</span> <span class="n">Resting</span>                   <span class="k">of</span> <span class="n">ParticipantStatusMessage</span>
    <span class="o">|</span> <span class="n">NeedsEmergencySupport</span>     <span class="k">of</span> <span class="n">ParticipantStatusMessage</span>
    <span class="o">|</span> <span class="n">Completed</span>                 <span class="k">of</span> <span class="n">ParticipantStatusMessage</span>
    <span class="o">|</span> <span class="n">DroppedOut</span>                <span class="k">of</span> <span class="n">ParticipantStatusMessage</span>
    <span class="o">|</span> <span class="n">Unknown</span>                   <span class="k">of</span> <span class="n">ParticipantStatusMessage</span>
    <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToStatusCombination</span><span class="bp">()</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">Continued</span> <span class="n">m</span>       <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span>
        <span class="o">|</span> <span class="n">Injured</span> <span class="n">s</span>         <span class="o">-&gt;</span>  <span class="k">match</span> <span class="n">s</span> <span class="k">with</span>
                                <span class="o">|</span> <span class="n">Continued</span> <span class="n">m</span>               <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span>
                                <span class="o">|</span> <span class="n">Resting</span> <span class="n">m</span>                 <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">3</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span>
                                <span class="o">|</span> <span class="n">NeedsEmergencySupport</span> <span class="n">m</span>   <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">4</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span>
                                <span class="o">|</span> <span class="n">DroppedOut</span> <span class="n">m</span>              <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">6</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span>
                                <span class="o">|</span> <span class="n">Unknown</span> <span class="n">m</span>                 <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span> 
                                <span class="o">|</span> <span class="o">_</span>                         <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="nn">String</span><span class="p">.</span><span class="n">Empty</span><span class="o">)</span>
        <span class="o">|</span> <span class="n">Resting</span> <span class="n">m</span>                 <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">3</span><span class="o">,</span> <span class="mi">3</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span>
        <span class="o">|</span> <span class="n">NeedsEmergencySupport</span> <span class="n">m</span>   <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">4</span><span class="o">,</span> <span class="mi">4</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span>
        <span class="o">|</span> <span class="n">Completed</span> <span class="n">m</span>               <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">5</span><span class="o">,</span> <span class="mi">5</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span>
        <span class="o">|</span> <span class="n">DroppedOut</span> <span class="n">m</span>
        <span class="o">|</span> <span class="n">Unknown</span> <span class="n">m</span>                 <span class="o">-&gt;</span> <span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">value</span> <span class="n">m</span><span class="o">)</span>
    <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToOptionName</span> <span class="bp">()</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">Continued</span> <span class="n">s</span>               <span class="o">-&gt;</span> <span class="s">&quot;Continued&quot;</span>
        <span class="o">|</span> <span class="n">Injured</span> <span class="n">s</span>                 <span class="o">-&gt;</span> <span class="s">&quot;Injured&quot;</span>
        <span class="o">|</span> <span class="n">Resting</span> <span class="n">s</span>                 <span class="o">-&gt;</span> <span class="s">&quot;Resting&quot;</span>
        <span class="o">|</span> <span class="n">NeedsEmergencySupport</span> <span class="n">s</span>   <span class="o">-&gt;</span> <span class="s">&quot;Needs Emergency Support&quot;</span>
        <span class="o">|</span> <span class="n">Completed</span> <span class="n">s</span>               <span class="o">-&gt;</span> <span class="s">&quot;Completed&quot;</span>
        <span class="o">|</span> <span class="n">DroppedOut</span> <span class="n">s</span>              <span class="o">-&gt;</span> <span class="s">&quot;Dropped Out&quot;</span>
        <span class="o">|</span> <span class="n">Unknown</span> <span class="n">s</span>                 <span class="o">-&gt;</span> <span class="s">&quot;Unknown&quot;</span>
    <span class="k">static</span> <span class="k">member</span> <span class="n">fromStatusCombo</span> <span class="n">s</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">s</span> <span class="k">with</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">Continued</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">))</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">Injured</span> <span class="o">(</span><span class="n">Continued</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">)))</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">3</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">Injured</span> <span class="o">(</span><span class="n">Resting</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">)))</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">4</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">Injured</span> <span class="o">(</span><span class="n">NeedsEmergencySupport</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">)))</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">2</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">Injured</span> <span class="o">(</span><span class="n">Unknown</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">)))</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">3</span><span class="o">,</span> <span class="mi">3</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">Resting</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">))</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">4</span><span class="o">,</span> <span class="mi">4</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">NeedsEmergencySupport</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">))</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">5</span><span class="o">,</span> <span class="mi">5</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">Completed</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">))</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">6</span><span class="o">,</span> <span class="mi">6</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">DroppedOut</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">))</span>
        <span class="o">|</span> <span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">Unknown</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">))</span>
        <span class="o">|</span> <span class="o">(_,</span> <span class="o">_,</span> <span class="n">m</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="n">Unknown</span> <span class="o">(</span><span class="nn">ParticipantStatusMessage</span><span class="p">.</span><span class="n">create</span> <span class="n">m</span><span class="o">))</span>
</pre></div>
<h4 id="the-final-participant-status-report-data-frame">The final participant status report data frame</h4>

<p>The final output for the <code>kissutil</code> will look like this:</p>

<blockquote>
<p>KG7SIO&gt;APDW15,WIDE1-1:{{P100923450004211In good shape!</p>
</blockquote>

<h2 id="parsing-received-messages">Parsing received messages</h2>

<p>FAPRS can produce an APRS message, and it can parse a TNC2MON formatted frame with a number of APRS data formats. To do this I used <a href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/active-patterns">Active Patterns</a>. The parser assumes the frames were produced by the DireWolf <code>kissutil</code>, which are in this format:</p>

<blockquote>
<p>[0] K1NRO-1&gt;APDW15,WIDE2-2:!4238.80NS07105.63W#PHG5630</p>
</blockquote>

<p>This is the same as what FAPRS produces for the <code>kissutil</code>, but includes the channel on which the message was received -- <code>[0]</code>.</p>

<h3 id="start-by-getting-the-frame-without-the-channel">Start by getting the frame without the channel</h3>
<div class="highlight"><pre><span></span><span class="c1">//Remove the channel from the frame</span>
<span class="k">let</span> <span class="o">(|</span><span class="n">Frame</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">record</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
    <span class="k">match</span> <span class="n">record</span> <span class="k">with</span>
    <span class="o">|</span> <span class="n">r</span> <span class="k">when</span> <span class="nn">String</span><span class="p">.</span><span class="n">IsNullOrWhiteSpace</span><span class="o">(</span><span class="n">r</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">None</span>
    <span class="o">|</span> <span class="n">r</span> <span class="k">when</span> <span class="n">r</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot; &quot;</span><span class="o">)</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="o">-&gt;</span> <span class="n">None</span> <span class="c1">//maybe return r because there was no channel and that&#39;s ok?</span>
    <span class="o">|</span> <span class="n">r</span> <span class="k">when</span> <span class="o">(</span><span class="n">r</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="n">r</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot; &quot;</span><span class="o">))).</span><span class="n">Trim</span><span class="bp">()</span><span class="o">.</span><span class="n">Length</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="o">((</span><span class="n">r</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="n">r</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot; &quot;</span><span class="o">))).</span><span class="n">Trim</span><span class="bp">()</span><span class="o">)</span>
    <span class="o">|</span> <span class="o">_</span> <span class="o">-&gt;</span> <span class="n">None</span>
</pre></div>
<h3 id="get-the-address-field-the-sender-and-destination">Get the address field -- the sender and destination</h3>
<div class="highlight"><pre><span></span><span class="k">let</span> <span class="o">(|</span><span class="n">Address</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">frame</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
    <span class="k">if</span> <span class="n">frame</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;:&quot;</span><span class="o">)</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="k">then</span> 
        <span class="n">None</span>
    <span class="k">else</span>
        <span class="n">Some</span> <span class="o">(</span><span class="n">frame</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">frame</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;:&quot;</span><span class="o">)))</span>
</pre></div>
<h3 id="get-the-sender-and-destination-out-of-the-address">Get the sender and destination out of the Address</h3>
<div class="highlight"><pre><span></span><span class="k">let</span> <span class="o">(|</span><span class="n">Sender</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">address</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
    <span class="k">if</span> <span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;&gt;&quot;</span><span class="o">)</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="k">then</span> <span class="n">None</span>
    <span class="k">else</span> <span class="n">Some</span> <span class="o">(</span><span class="n">address</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;&gt;&quot;</span><span class="o">)))</span>

<span class="k">let</span> <span class="o">(|</span><span class="n">Destination</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">address</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
    <span class="k">if</span> <span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;&gt;&quot;</span><span class="o">)</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;,&quot;</span><span class="o">)</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="k">then</span> <span class="n">None</span>
    <span class="k">else</span> <span class="n">Some</span> <span class="o">(</span><span class="n">address</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;&gt;&quot;</span><span class="o">)</span> <span class="o">+</span> <span class="mi">1</span><span class="o">,</span> <span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;,&quot;</span><span class="o">)</span> <span class="o">-</span> <span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;&gt;&quot;</span><span class="o">)</span> <span class="o">-</span> <span class="mi">1</span><span class="o">))</span>
</pre></div>
<h3 id="get-the-path-out-of-the-frame">Get the Path out of the Frame</h3>
<div class="highlight"><pre><span></span><span class="k">let</span> <span class="o">(|</span><span class="n">Path</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">address</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="o">(</span><span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;&gt;&quot;</span><span class="o">)</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;,&quot;</span><span class="o">)</span> <span class="o">&gt;</span> <span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;&gt;&quot;</span><span class="o">)</span> <span class="k">then</span>
        <span class="n">Some</span> <span class="o">(</span><span class="n">address</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="n">address</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;,&quot;</span><span class="o">)</span> <span class="o">+</span> <span class="mi">1</span><span class="o">).</span><span class="n">Split</span><span class="o">(</span><span class="sc">&#39;,&#39;</span><span class="o">))</span>
    <span class="k">else</span>
        <span class="n">None</span>
</pre></div>
<h3 id="get-the-message-out-of-the-frame">Get the Message out of the Frame</h3>
<div class="highlight"><pre><span></span><span class="k">let</span> <span class="o">(|</span><span class="n">Message</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">frame</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
    <span class="k">if</span> <span class="n">frame</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;:&quot;</span><span class="o">)</span> <span class="o">&lt;</span> <span class="mi">1</span> <span class="k">then</span> 
        <span class="n">None</span>
    <span class="k">else</span>
        <span class="n">Some</span> <span class="o">(</span><span class="n">frame</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="n">frame</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="s">&quot;:&quot;</span><span class="o">)</span> <span class="o">+</span> <span class="mi">1</span><span class="o">))</span>
</pre></div>
<h3 id="parse-a-position-report">Parse a position report</h3>

<h4 id="get-the-latitude-out-of-the-message">Get the Latitude out of the Message</h4>
<div class="highlight"><pre><span></span><span class="k">let</span> <span class="o">(|</span><span class="n">Latitude</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">msg</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span> 
    <span class="k">let</span> <span class="nv">parseLatitude</span> <span class="o">(</span><span class="n">posRpt</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
        <span class="k">let</span> <span class="nv">lat</span> <span class="o">=</span> <span class="n">posRpt</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="mi">8</span><span class="o">)</span>
        <span class="k">match</span> <span class="n">lat</span><span class="o">.</span><span class="n">EndsWith</span><span class="o">(</span><span class="s">&quot;N&quot;</span><span class="o">),</span> <span class="n">lat</span><span class="o">.</span><span class="n">EndsWith</span><span class="o">(</span><span class="s">&quot;S&quot;</span><span class="o">)</span> <span class="k">with</span>
        <span class="o">|</span> <span class="k">true</span><span class="o">,</span> <span class="k">false</span>   <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">lat</span>
        <span class="o">|</span> <span class="k">false</span><span class="o">,</span> <span class="k">true</span>   <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">lat</span>
        <span class="o">|</span> <span class="o">_</span>             <span class="o">-&gt;</span> <span class="n">None</span>
    <span class="k">match</span> <span class="n">getAPRSDataTypeIdentifier</span> <span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span><span class="mi">1</span><span class="o">))</span> <span class="k">with</span>
    <span class="o">|</span> <span class="n">Some</span> <span class="n">id</span>   <span class="o">-&gt;</span>  <span class="k">match</span> <span class="n">id</span> <span class="k">with</span>
                    <span class="o">|</span> <span class="n">PositionReportWithoutTimeStampWithMessaging</span>   <span class="o">-&gt;</span> <span class="o">(</span><span class="n">parseLatitude</span> <span class="n">msg</span><span class="o">)</span>
                    <span class="o">|</span> <span class="n">PositionReportWithoutTimeStampNoMessaging</span>     <span class="o">-&gt;</span> <span class="o">(</span><span class="n">parseLatitude</span> <span class="n">msg</span><span class="o">)</span>
                    <span class="o">|</span> <span class="o">_</span>                                             <span class="o">-&gt;</span> <span class="n">None</span>
        <span class="o">|</span> <span class="n">None</span>     <span class="o">-&gt;</span> <span class="n">None</span> <span class="c1">//We do not have a position report and therefore no latitude</span>
</pre></div>
<h4 id="get-the-longitude-out-of-the-message">Get the Longitude out of the Message</h4>
<div class="highlight"><pre><span></span><span class="k">let</span> <span class="o">(|</span><span class="n">Longitude</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">msg</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">parseLongitude</span> <span class="o">(</span><span class="n">posRpt</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
        <span class="k">let</span> <span class="nv">lon</span> <span class="o">=</span> <span class="n">posRpt</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">10</span><span class="o">,</span> <span class="mi">9</span><span class="o">)</span> 
        <span class="k">match</span> <span class="n">lon</span><span class="o">.</span><span class="n">EndsWith</span><span class="o">(</span><span class="s">&quot;W&quot;</span><span class="o">),</span> <span class="n">lon</span><span class="o">.</span><span class="n">EndsWith</span><span class="o">(</span><span class="s">&quot;E&quot;</span><span class="o">)</span> <span class="k">with</span> 
        <span class="o">|</span> <span class="k">true</span><span class="o">,</span> <span class="k">false</span>   <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">lon</span>
        <span class="o">|</span> <span class="k">false</span><span class="o">,</span> <span class="k">true</span>   <span class="o">-&gt;</span> <span class="n">Some</span> <span class="n">lon</span>
        <span class="o">|</span> <span class="o">_</span>             <span class="o">-&gt;</span> <span class="n">None</span>

    <span class="k">match</span> <span class="n">msg</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">9</span><span class="o">,</span><span class="mi">1</span><span class="o">)</span> <span class="k">with</span>
    <span class="o">|</span> <span class="s">&quot;/&quot;</span> <span class="o">-&gt;</span> <span class="n">parseLongitude</span> <span class="n">msg</span>
    <span class="o">|</span> <span class="o">_</span> <span class="o">-&gt;</span> <span class="n">None</span>
</pre></div>
<h4 id="get-the-symbol-out-of-the-message">Get the Symbol out of the Message</h4>
<div class="highlight"><pre><span></span><span class="k">let</span> <span class="o">(|</span><span class="n">Symbol</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">msg</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
    <span class="c1">//TODO check that the previous char was a W or E meaning that it was probably and APRS lat/lon</span>
    <span class="k">match</span> <span class="n">msg</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">18</span><span class="o">,</span><span class="mi">1</span><span class="o">)</span> <span class="k">with</span>
    <span class="o">|</span> <span class="s">&quot;W&quot;</span> <span class="o">-&gt;</span> <span class="nn">SymbolCode</span><span class="p">.</span><span class="n">fromSymbol</span> <span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">19</span><span class="o">,</span><span class="mi">1</span><span class="o">).</span><span class="n">ToCharArray</span><span class="bp">()</span><span class="o">.[</span><span class="mi">0</span><span class="o">])</span> <span class="c1">//  getSymbolCode (msg.Substring(19,1).ToCharArray().[0])</span>
    <span class="o">|</span> <span class="s">&quot;E&quot;</span> <span class="o">-&gt;</span> <span class="nn">SymbolCode</span><span class="p">.</span><span class="n">fromSymbol</span> <span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">19</span><span class="o">,</span><span class="mi">1</span><span class="o">).</span><span class="n">ToCharArray</span><span class="bp">()</span><span class="o">.[</span><span class="mi">0</span><span class="o">])</span>
    <span class="o">|</span> <span class="o">_</span> <span class="o">-&gt;</span> <span class="n">None</span>
</pre></div>
<h4 id="get-the-comment-out-of-the-message">Get the Comment out of the Message</h4>
<div class="highlight"><pre><span></span><span class="k">let</span> <span class="o">(|</span><span class="n">Comment</span><span class="o">|_|)</span> <span class="o">(</span><span class="n">symbol</span><span class="o">:</span><span class="kt">char</span><span class="o">)</span> <span class="o">(</span><span class="n">msg</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">comment</span> <span class="o">=</span> <span class="n">msg</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="n">msg</span><span class="o">.</span><span class="n">IndexOf</span><span class="o">(</span><span class="n">symbol</span><span class="o">)</span> <span class="o">+</span> <span class="mi">1</span><span class="o">).</span><span class="n">Trim</span><span class="bp">()</span>
    <span class="k">if</span> <span class="n">comment</span> <span class="o">=</span> 
        <span class="nn">String</span><span class="p">.</span><span class="n">Empty</span> 
    <span class="k">then</span>    
        <span class="n">None</span> 
    <span class="k">else</span> 
        <span class="n">Some</span> <span class="n">comment</span>
</pre></div>
<h4 id="the-tests">The Tests</h4>

<p>To see the active patterns in action, check out <code>faprs.tests</code> <code>TNC2MONActivePatternsTests</code>.</p>

<p>For example</p>
<div class="highlight"><pre><span></span><span class="n">testCase</span> <span class="s">&quot;Can get message part of well formed frame with message&quot;</span> <span class="o">&lt;|</span> <span class="k">fun</span> <span class="o">_</span> <span class="o">-&gt;</span>
    <span class="k">let</span> <span class="nv">result</span> <span class="o">=</span>
        <span class="k">match</span> <span class="s">&quot;[0] KG7SIO-7&gt;APRD15,WIDE1-1,TCPXX*,qAX,CWOP-2:=03216.4N/011057.3Wb,b&gt;,lah:blah /fishcakes&quot;</span> <span class="k">with</span>
        <span class="o">|</span> <span class="nn">TNC2MonActivePatterns</span><span class="p">.</span><span class="n">Message</span> <span class="n">m</span> <span class="o">-&gt;</span> <span class="n">m</span>
        <span class="o">|</span> <span class="o">_</span> <span class="o">-&gt;</span> <span class="nn">String</span><span class="p">.</span><span class="n">Empty</span>
    <span class="nn">Expect</span><span class="p">.</span><span class="n">equal</span> <span class="n">result</span> <span class="s">&quot;=03216.4N/011057.3Wb,b&gt;,lah:blah /fishcakes&quot;</span> <span class="s">&quot;Message does not match&quot;</span>
</pre></div>
<h2 id="sending-messages-and-a-demo-proof-of-concept">Sending messages and a demo (proof of concept)</h2>

<p>To send messages we can use faprs.cli, but it only supports the <code>unformatted</code> and <code>position report without timestamp</code> APRS data formats at the moment. For the demo I am sending a position report. </p>

<blockquote>
<p>See <a href="https://github.com/MarneeDear/FAPRS/blob/master/README.md">the project&#39;s README</a> for more on how to run and use FAPRS</p>
</blockquote>

<p>I will use <code>dotnet run</code> to run the CLI command, like this:</p>
<div class="highlight"><pre><span></span>dotnet run --project src/faprs.cli/ -- --save-to XMIT --sender KG7SIO-7 --destination KG7SIL --path WIDE1-1 --rpt latitude <span class="m">32</span>.2217 longitude -110.9265 symbol b comment <span class="s2">&quot;My submission for the applied F# challenge.&quot;</span>
</pre></div>
<p>The CLI takes latitude and longitude in decimal degrees and converts it to APRS format.</p>

<p>In this demo I am expecting an iGate and digipeater in my area (<code>N7HND</code>) to receive my signal and forward it to <a href="https://aprs.fi">aprs.fi</a>, where I will be able to see my station on a map, and see the packets that were received.</p>

<h3 id="steps">Steps:</h3>

<ol>
<li>Tune my radio to the <a href="http://www.aprs.org/">recommended APRS frequency</a> <code>144.390</code> so other stations near me can pick up my signal.</li>
<li>Attach the radio to the computer audio. This is the audio controller that came with my Dell. I can select the &quot;headset&quot; option which lets me transmit and receive, or the &quot;external speaker&quot; option which only lets me transmit.</li>
<li>Start <code>DireWolf</code></li>
<li>Start the <code>kissutil</code></li>
<li>Enter the CLI command</li>
<li>Check that <code>DireWolf</code> and the <code>kissutil</code> read the file that was generated and sent the message </li>
<li>Check with aprs.fi to see if the message made it through the local digipeater</li>
</ol>

<p><img alt="Imgur" src="https://i.imgur.com/QoR9wYK.gif" /></p>

<p><img alt="Imgur" src="https://i.imgur.com/ksav8IY.png" /></p>

<h2 id="other-resources">Other Resources</h2>

<blockquote>
<p><a href="https://www.youtube.com/watch?v=OgFBXfwmKYc" title="Everything Ham Radio Podcast"><img alt="History of APRS" src="https://img.youtube.com/vi/OgFBXfwmKYc/0.jpg" /></a></p>
</blockquote>

<p><em>click the video to watch on You Tube</em></p>

<h3 id="prior-art">Prior Art</h3>

<p><a href="https://github.com/ampledata/aprs">Python APRS Module</a></p>

<p><a href="https://aprsdroid.org/">APRSdroid</a></p>
]]></content:encoded>
      </item>
      <item>
        <guid>https://marnee.silvrback.com/cross-platform-development-with-net-core-and-f#46830</guid>
          <pubDate>Sat, 30 Mar 2019 17:36:37 -0700</pubDate>
        <link>https://marnee.silvrback.com/cross-platform-development-with-net-core-and-f</link>
        <title>Cross-platform development with .NET Core and F#</title>
        <description>University of Arizona IT Summit Workshop</description>
        <content:encoded><![CDATA[<p>This is my script for a workshop I will be leading at the <a href="https://itsummit.arizona.edu/interactive">University of Arizona IT Summit InterActive day</a></p>

<h1 id="it-summit-interactive-workshop">IT Summit interActive Workshop</h1>

<h2 id="workshop-leader">Workshop Leader</h2>

<p>Marnee Dearman</p>

<p>Chief Applications Architect</p>

<p>College of Medicine</p>

<h2 id="summary">Summary</h2>

<p>Cross-platform software development with .NET Core and F#.</p>

<p>This is the script I will follow but it can be used to learn or practice on your own.</p>

<p><img title="Ooompa loomp doopity doo." alt="Willy Wonka" src="https://i.imgur.com/wZbJs1m.jpg" /></p>

<h2 id="topics">Topics</h2>

<ul>
<li>.NET Core

<ul>
<li>Start a new .NET Core project from the standard templates</li>
<li>Start a new .NET Core project from imported templates</li>
<li>Create a solution file and associate projects</li>
<li>Adding dependencies to a project</li>
<li>Restoring and building a project</li>
<li>Running your application</li>
<li>Running tests</li>
<li>Publishing your application to different target systems (Linux, Linux ARM, MacOS, Windows)</li>
</ul></li>
<li>F#

<ul>
<li>Union types and record types for elegant domain modeling and enforcing constraints</li>
<li>Using <a href="https://fsprojects.github.io/Argu/">Argu</a> to quickly build a command-line tool</li>
<li>Using <a href="https://github.com/haf/expecto">Expecto</a> to write tests</li>
<li>Using <a href="https://safe-stack.github.io/">SAFE stack</a> to create web applications</li>
<li>Using <a href="https://fake.build/">FAKE</a> to build, test, run, and deploy applications</li>
</ul></li>
</ul>

<h2 id="workshop-requirements">Workshop requirements</h2>

<ul>
<li>.NET Core 2.2 SDK

<ul>
<li>Get the latest version <a href="https://dotnet.microsoft.com/download">here</a>. Select your operating system and follow the installation instructions.</li>
</ul></li>
</ul>

<h2 id="workshop-setup">Workshop setup</h2>

<p>We need a folder to work in. Let&#39;s create one.</p>

<p>Find a file system location that works for you. I am going to do this in my Windows home directory on my Windows Subsystem for Linux environment. This is equivalent to my Windows home directory. If you are on Windows, you can use that too. Use a location that works for you.</p>

<h3 id="ubuntu">Ubuntu</h3>
<div class="highlight"><pre><span></span>marnee@DESKTOP-BBKBQMF:/mnt/c/Users/Marnee$ <span class="nb">pwd</span>
/mnt/c/Users/Marnee
</pre></div>
<h3 id="windows-command-line">Windows command-line:</h3>
<div class="highlight"><pre><span></span>Marnee@DESKTOP-BBKBQMF C:<span class="se">\U</span>sers<span class="se">\M</span>arnee
&gt; <span class="nb">cd</span>
C:<span class="se">\U</span>sers<span class="se">\M</span>arnee
</pre></div>
<p>Use the command line commands that work for your environment.</p>

<h3 id="bash-macos-terminal">BaSH/MacOS Terminal</h3>
<div class="highlight"><pre><span></span>mkdir interactive-workshop
<span class="nb">cd</span> interactive-workshop
</pre></div>
<h3 id="dos-windows-command-line">DOS/Windows Command Line</h3>
<div class="highlight"><pre><span></span>mkdir interactive-workshop
cd interactive-workshop
</pre></div>
<p>This is the folder where we will do all of our work for the rest of the workshop.</p>

<p>(wait for green stickies)</p>

<h2 id="net-core-cli">.NET Core CLI</h2>

<p>The .NET Core CLI has a number of commands to help you:</p>

<ul>
<li>Scaffold a new project from a built in template or custom project template</li>
<li>Add and manage packages and dependencies</li>
<li>Restore dependencies</li>
<li>Build &amp; compile projects</li>
<li>Publish applications</li>
<li>Build and run tests</li>
<li>Run applications</li>
<li>Auto-rebuild and re-load while running (easier to make changes)</li>
</ul>

<h2 id="dotnet-new">dotnet new</h2>

<p><code>dotnet new</code> is what we use to scaffold a new project.</p>

<p>On your command line enter:</p>
<div class="highlight"><pre><span></span>dotnet new
</pre></div>
<p>You should see a list of options and templates. Let&#39;s look at the options:</p>
<div class="highlight"><pre><span></span>Options:
  -h, --help          Displays <span class="nb">help</span> <span class="k">for</span> this command.
  -l, --list          Lists templates containing the specified name. If no name is specified, lists all templates.
  -n, --name          The name <span class="k">for</span> the output being created. If no name is specified, the name of the current directory is used.
  -o, --output        Location to place the generated output.
  -i, --install       Installs a <span class="nb">source</span> or a template pack.
  -u, --uninstall     Uninstalls a <span class="nb">source</span> or a template pack.
  --nuget-source      Specifies a NuGet <span class="nb">source</span> to use during install.
  --type              Filters templates based on available types. Predefined values are <span class="s2">&quot;project&quot;</span>, <span class="s2">&quot;item&quot;</span> or <span class="s2">&quot;other&quot;</span>.
  --dry-run           Displays a summary of what would happen <span class="k">if</span> the given <span class="nb">command</span> line were run <span class="k">if</span> it would result in a template creation.
  --force             Forces content to be generated even <span class="k">if</span> it would change existing files.
  -lang, --language   Filters templates based on language and specifies the language of the template to create.
</pre></div>
<p><code>--l, --list Lists templates containing the specified name. If no name is specified, lists all templates.</code></p>

<p>Let&#39;s use this option to see a list of templates. We will use templates to create our projects. Let&#39;s see what kinds of templates we have:</p>

<p><em>Your templates may look different than mine. That is ok.</em></p>
<div class="highlight"><pre><span></span>dotnet new --list
</pre></div>
<p>Result</p>
<div class="highlight"><pre><span></span>Templates                                         Short Name         Language          Tags
------------------------------------------------------------------------------------------------------------------------------------------------
Console Application                               console            <span class="o">[</span>C#<span class="o">]</span>, F#, VB      Common/Console
Class library                                     classlib           <span class="o">[</span>C#<span class="o">]</span>, F#, VB      Common/Library
Unit Test Project                                 mstest             <span class="o">[</span>C#<span class="o">]</span>, F#, VB      Test/MSTest
NUnit <span class="m">3</span> Test Project                              nunit              <span class="o">[</span>C#<span class="o">]</span>, F#, VB      Test/NUnit
NUnit <span class="m">3</span> Test Item                                 nunit-test         <span class="o">[</span>C#<span class="o">]</span>, F#, VB      Test/NUnit
xUnit Test Project                                xunit              <span class="o">[</span>C#<span class="o">]</span>, F#, VB      Test/xUnit
Razor Page                                        page               <span class="o">[</span>C#<span class="o">]</span>              Web/ASP.NET
MVC ViewImports                                   viewimports        <span class="o">[</span>C#<span class="o">]</span>              Web/ASP.NET
MVC ViewStart                                     viewstart          <span class="o">[</span>C#<span class="o">]</span>              Web/ASP.NET
ASP.NET Core Empty                                web                <span class="o">[</span>C#<span class="o">]</span>, F#          Web/Empty
ASP.NET Core Web App <span class="o">(</span>Model-View-Controller<span class="o">)</span>      mvc                <span class="o">[</span>C#<span class="o">]</span>, F#          Web/MVC
ASP.NET Core Web App                              webapp             <span class="o">[</span>C#<span class="o">]</span>              Web/MVC/Razor Pages
ASP.NET Core with Angular                         angular            <span class="o">[</span>C#<span class="o">]</span>              Web/MVC/SPA
ASP.NET Core with React.js                        react              <span class="o">[</span>C#<span class="o">]</span>              Web/MVC/SPA
ASP.NET Core with React.js and Redux              reactredux         <span class="o">[</span>C#<span class="o">]</span>              Web/MVC/SPA
Razor Class Library                               razorclasslib      <span class="o">[</span>C#<span class="o">]</span>              Web/Razor/Library/Razor Class Library
ASP.NET Core Web API                              webapi             <span class="o">[</span>C#<span class="o">]</span>, F#          Web/WebAPI
global.json file                                  globaljson                           Config
NuGet Config                                      nugetconfig                          Config
Web Config                                        webconfig                            Config
Solution File                                     sln                                  Solution
</pre></div>
<p>We have a lot of built-in templates. </p>

<p>The first column is the template, the second is the short name, which is used in the <code>dotnet new</code> command, and the third is the language supported by that template. Notice we have lots of F# templates available.</p>

<p>These are the templates we are going to start with:</p>
<div class="highlight"><pre><span></span>Templates                                         Short Name         Language          Tags
------------------------------------------------------------------------------------------------------------------------------------------------
Console Application                               console            <span class="o">[</span>C#<span class="o">]</span>, F#, VB      Common/Console
Class library                                     classlib           <span class="o">[</span>C#<span class="o">]</span>, F#, VB      Common/Library
Solution File                                     sln                                  Solution
</pre></div>
<h3 id="console-application">Console Application</h3>

<p>Scaffolds a project you can use to build a CLI or an executable. We will see more later.</p>

<h3 id="class-library">Class Library</h3>

<p>This is a class library that can be referenced by other projects. It cannot be executed, or run.</p>

<h3 id="solution-file">Solution File</h3>

<p>A solution file defines a set of projects that are related to each other. This is helpful in IDEs like Visual Studio Code and Visual Studio. The IDEs can use the solution file to organize your projects. The solution file can also help with the compiler.</p>

<p><img title="Cats run the Internet!" alt="Business cat" src="https://i.imgur.com/jQdT64R.jpg" /></p>

<h2 id="use-dotnet-new-to-scaffold-projects">Use <code>dotnet new</code> to scaffold projects</h2>

<p>First we need a solution structure. This is what I will use and is similar to what I usually do. This mostly follows the principles of <code>clean architecture</code> or <code>onion architecture</code>.</p>
<div class="highlight"><pre><span></span>interactive-workshop
                    |
                    workshop.sln (file)
                                        |
                                        src (folder)
                                                    |
                                                    workshop.cli (folder)
                                                    workshop.domain (folder)
                                                    workshop.test (folder)
                                                    workshop.web (folder)
</pre></div>
<p>We will talk about all of these parts later, but first let&#39;s setup the folder structure.</p>

<p>On the bash command-line this looks like this:</p>
<div class="highlight"><pre><span></span>mkdir src
mkdir src/workshop.cli
mkdir src/workshop.domain
mkdir src/workshop.test
mkdir src/workshop.web
</pre></div>
<p>(Create your folder structure now)</p>

<p>(Wait for green stickies)</p>

<h3 id="scaffold-a-new-console-application">Scaffold a new console application</h3>

<p>Let&#39;s create a <code>Console Application</code> using the F# language. It is going to live in the <code>workshop.cli</code> folder.</p>

<p>By default, <code>dotnet new</code> names your project according to the folder in which you are creating the project. So to get a new <code>workshop.cli</code>, use <code>cd</code> to get into <code>src/workshop.cli</code>, first.</p>
<div class="highlight"><pre><span></span><span class="nb">cd</span> src/workshop.cli
dotnet new console -lang F#
</pre></div>
<p>A lot of stuff happened but you should see a confirmation message in the output.</p>
<div class="highlight"><pre><span></span>The template &quot;Console Application&quot; was created successfully.
</pre></div>
<p>Great! You just created a console app. Let&#39;s see what <code>dotnet new</code> created:</p>

<p>BaSH/Terminal</p>
<div class="highlight"><pre><span></span>ls -la
</pre></div>
<p>DoS</p>
<div class="highlight"><pre><span></span>dir
</pre></div><div class="highlight"><pre><span></span>drwxrwxrwx 1 marnee marnee 512 Mar 24 11:27 .
drwxrwxrwx 1 marnee marnee 512 Mar 24 11:25 ..
drwxrwxrwx 1 marnee marnee 512 Mar 24 11:27 obj
-rwxrwxrwx 1 marnee marnee 172 Mar 24 11:26 Program.fs
-rwxrwxrwx 1 marnee marnee 252 Mar 24 11:26 workshop.cli.fsproj
</pre></div>
<h4 id="program-fs">Program.fs</h4>

<p>This where all your code will go. This is the entry point for execution.</p>

<h4 id="workshop-cli-fsproj">workshop.cli.fsproj</h4>

<p><code>proj</code> files are common to all .NET projects. This defines what files are associated with the project, and the compiler will use the <code>proj</code> file to know what code to compile.</p>

<h2 id="run-a-console-project">Run a console project</h2>

<p>Let&#39;s see what this workshop.cli will do.</p>

<h3 id="dotnet-run">dotnet run</h3>

<p><code>dotnet run</code> will do this by default:</p>

<ol>
<li>restore dependencies</li>
<li><p>build (compile) the project</p>

<p>a. This means it will turn the code into a binary file.</p></li>
<li><p>run the application</p></li>
</ol>

<p>Let&#39;s see what this command can do, first.</p>
<div class="highlight"><pre><span></span>dotnet run -h
</pre></div>
<p>You should see the usage and options.</p>
<div class="highlight"><pre><span></span>Usage: dotnet run <span class="o">[</span>options<span class="o">]</span> <span class="o">[[</span>--<span class="o">]</span> &lt;additional arguments&gt;...<span class="o">]]</span>
</pre></div><div class="highlight"><pre><span></span>Options:
  -h, --help                            Show <span class="nb">command</span> line help.
  -c, --configuration &lt;CONFIGURATION&gt;   The configuration to run <span class="k">for</span>. The default <span class="k">for</span> most projects is <span class="s1">&#39;Debug&#39;</span>.
  -f, --framework &lt;FRAMEWORK&gt;           The target framework to run <span class="k">for</span>. The target framework must also be specified in the project file.
  -p, --project                         The path to the project file to run <span class="o">(</span>defaults to the current directory <span class="k">if</span> there is only one project<span class="o">)</span>.
  --launch-profile                      The name of the launch profile <span class="o">(</span><span class="k">if</span> any<span class="o">)</span> to use when launching the application.
  --no-launch-profile                   Do not attempt to use launchSettings.json to configure the application.
  --no-build                            Do not build the project before running. Implies --no-restore.
  --no-restore                          Do not restore the project before building.
  -v, --verbosity &lt;LEVEL&gt;               Set the MSBuild verbosity level. Allowed values are q<span class="o">[</span>uiet<span class="o">]</span>, m<span class="o">[</span>inimal<span class="o">]</span>, n<span class="o">[</span>ormal<span class="o">]</span>, d<span class="o">[</span>etailed<span class="o">]</span>, and diag<span class="o">[</span>nostic<span class="o">]</span>.
  --runtime &lt;RUNTIME_IDENTIFIER&gt;        The target runtime to restore packages <span class="k">for</span>.
  --no-dependencies                     Do not restore project-to-project references and only restore the specified project.
  --force                               Force all dependencies to be resolved even <span class="k">if</span> the last restore was successful.
                                        This is equivalent to deleting project.assets.json.
Additional Arguments:
  Arguments passed to the application that is being run.
</pre></div>
<p>Cool! We have a lot of options. Let&#39;s try running the default options first.</p>

<p>Remember the usage? <code>dotnet run -h</code> can help.</p>
<div class="highlight"><pre><span></span>dotnet run
</pre></div>
<p>By default, if no <code>-p, --project</code> option passed, <code>dotnet</code> will try to find a project file in the current folder, and if it finds an executeable project, it will do the run on that application.</p>

<p>If it seems slow, this is ok. That is because <code>dotnet run</code> is restoring and building first.</p>

<p>If the <code>run</code> worked, you should see some friendly output:</p>
<div class="highlight"><pre><span></span>Hello World from F#!
</pre></div>
<p>Great success! You just:</p>

<ol>
<li>Scaffolded a console application with <code>dotnet new</code></li>
<li>Ran the application with <code>dotnet run</code></li>
<li>And it worked!</li>
</ol>

<p><img title="First step achievement unlocked" alt="demo worked" src="https://i.imgur.com/0DvVlTt.jpg" /></p>

<p>(wait for stickies)</p>

<h2 id="class-library">Class Library</h2>

<p>Let&#39;s go through the steps to start a class library.</p>

<blockquote>
<p>Remember that <code>dotnet new</code> will scaffold a project with the same name as the containing folder, so remember to cd into the desired folder before running the command.</p>
</blockquote>

<p>Let&#39;s first go to the <code>workshop.domain</code> folder we created earlier. This is where we will scaffold our class library.</p>
<div class="highlight"><pre><span></span><span class="nb">cd</span> ../workshop.domain
</pre></div>
<p>(wait for green stickies)</p>

<p>Do you remember how to see the templates?</p>
<div class="highlight"><pre><span></span>dotnet new --list
</pre></div>
<p>The Class Library templates short name is <code>classlib</code>.</p>

<p>Remember the usage?</p>
<div class="highlight"><pre><span></span>Examples:
    dotnet new mvc --auth Individual
    dotnet new nunit-test
    dotnet new --help
</pre></div>
<p>What command do we use to create a class library using F#?</p>

<blockquote>
<p>The default language is C# so don&#39;t forget to specify the language if you want to use something else.</p>
</blockquote>
<div class="highlight"><pre><span></span>dotnet new classlib -lang F#
</pre></div>
<p>Let&#39;s see what it created.</p>

<p>BaSH/Terminal</p>
<div class="highlight"><pre><span></span>ls -la
</pre></div>
<p>DoS</p>
<div class="highlight"><pre><span></span>dir
</pre></div>
<p>You should see the new project files.</p>
<div class="highlight"><pre><span></span>-rwxrwxrwx 1 marnee marnee 101 Mar 24 13:46 Library.fs
drwxrwxrwx 1 marnee marnee 512 Mar 24 13:46 obj
-rwxrwxrwx 1 marnee marnee 219 Mar 24 13:46 workshop.domain.fsproj
</pre></div>
<p>(wait for stickies)</p>

<p>Here we see the <code>proj</code> file again. And a file called <code>Libary.fs</code> with a bit of code in it.</p>

<p>Since Class Libraries are not executable, we can&#39;t run <code>dotnet run</code> on it. But we can reference it in an executable project, like a console application, and reference and use the class library in that project.</p>

<blockquote>
<p>You can&#39;t access code in a separate project without referencing in.</p>

<p>References go in the <code>proj</code> file using certain XML elements and attributes.</p>
</blockquote>

<p>Let&#39;s try that.</p>

<h3 id="dotnet-add">dotnet add</h3>

<p>First, go up one level to the <code>src</code> file. This will make it easier to write the commands.</p>
<div class="highlight"><pre><span></span><span class="nb">cd</span> ..
</pre></div>
<p>Let&#39;s reference <code>workshop.domain</code> in <code>workshop.cli</code>.</p>

<p>To do that we use the <code>dotnet add</code> command. Let&#39;s see the usage and options.</p>
<div class="highlight"><pre><span></span>dotnet add -h
</pre></div><div class="highlight"><pre><span></span>Usage: dotnet add [options] &lt;PROJECT&gt; [command]
</pre></div><div class="highlight"><pre><span></span>Arguments:
  &lt;PROJECT&gt;   The project file to operate on. If a file is not specified, the command will search the current directory for one.
</pre></div>
<p><code>&lt;PROJECT&gt;</code> is the path/project file to the project to which you want to add the reference. In this case it is the path to the <code>workshop.cli</code> project file. We will see this later.</p>
<div class="highlight"><pre><span></span>Commands:
  package &lt;PACKAGE_NAME&gt;     Add a NuGet package reference to the project.
  reference &lt;PROJECT_PATH&gt;   Add a project-to-project reference to the project.
</pre></div>
<p>is the path to the class library you want to use in your project.</p>

<p>How would we reference <code>workshop.domain</code> from <code>workshop.cli</code>?</p>
<div class="highlight"><pre><span></span>dotnet add workshop.cli reference workshop.domain
</pre></div>
<blockquote>
<p>Pro tip: use tab complete. Type out a few characters and then hit tab. The command line will try to complete the path for you. This is available in both BaSH and DoS.</p>
</blockquote>

<p>You should see this output.</p>
<div class="highlight"><pre><span></span>Reference `..\workshop.domain\workshop.domain.fsproj` added to the project.
</pre></div>
<p>Let&#39;s see what happened to the proj file. Let&#39;s print the content to the screen.</p>

<p>BaSH/Terminal</p>
<div class="highlight"><pre><span></span>cat workshop.cli/workshop.cli.fsproj
</pre></div>
<p>DoS</p>
<div class="highlight"><pre><span></span>type workshop.cli\workshop.cli.fsproj
</pre></div>
<p>Notice this XML element:</p>
<div class="highlight"><pre><span></span><span class="nt">&lt;ItemGroup&gt;</span>
    <span class="nt">&lt;ProjectReference</span> <span class="na">Include=</span><span class="s">&quot;..\workshop.domain\workshop.domain.fsproj&quot;</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/ItemGroup&gt;</span>
</pre></div>
<p>(wait for stickies)</p>

<p>(resolve problems if any)</p>

<h2 id="review">Review</h2>

<p>We learned how to:</p>

<ul>
<li>scaffold a console and class library using <code>dotnet new</code></li>
<li>run a console app using <code>dotnet run</code></li>
<li>add a reference to the class library using <code>dotnet add</code></li>
</ul>

<p>(take a break and answer questions)</p>

<p><img title="Things are getting pretty serious" alt="review1" src="https://i.imgur.com/7Nj8HcR.jpg" /></p>

<h2 id="scaffold-a-test-project">Scaffold a test project</h2>

<p>First, get yourself into the <code>workshop.test</code> folder.</p>
<div class="highlight"><pre><span></span><span class="nb">cd</span> workshop.test
</pre></div>
<blockquote>
<p>Pro tip: tab complete is your best friend.</p>
</blockquote>

<p><code>dotnet new</code> comes with templates for creating xUnit and nUnit test projects. Those are great, but let&#39;s use something <code>functional programming oriented.</code> Let&#39;s use <code>Expecto</code>. </p>

<p>Expecto publishes a <code>dotnet</code> template that we can install and then use.</p>

<p>Go to the <a href="https://github.com/MNie/Expecto.Template">Expecto template on Github</a>.</p>

<blockquote>
<p>There are <a href="https://github.com/dotnet/templating/wiki/Available-templates-for-dotnet-new">lots of different templates available</a>. </p>
</blockquote>

<p>You&#39;ll see instructions on how to install the template from Nuget (Nuget is a .NET package manager and repository).</p>
<div class="highlight"><pre><span></span>dotnet new -i Expecto.Template
</pre></div>
<p><code>-i</code> is the <code>option</code> for installing new templates.</p>

<p>You should see output that looks like the <code>dotnet new -h</code> command. Notice in the templates list that there is a new template:</p>
<div class="highlight"><pre><span></span>Expecto .net core Template                        expecto            F#                Test
</pre></div>
<p>Now we can use it to scaffold a new Expecto project.</p>
<div class="highlight"><pre><span></span>dotnet new expecto -lang F#
</pre></div>
<p>Let&#39;s see what it created.</p>

<p>BaSH/Terminal</p>
<div class="highlight"><pre><span></span>ls -la
</pre></div>
<p>DoS</p>
<div class="highlight"><pre><span></span>dir
</pre></div><div class="highlight"><pre><span></span>-rwxrwxrwx 1 marnee marnee  123 Mar 24 20:41 Main.fs*
-rwxrwxrwx 1 marnee marnee 1206 Mar 24 20:41 Sample.fs*
-rwxrwxrwx 1 marnee marnee  639 Mar 24 20:41 workshop.test.fsproj*
</pre></div>
<p>Notice that we have a <code>proj</code> file and two sample test files.</p>

<p>(wait for stickies)</p>

<h3 id="run-the-tests">Run the tests</h3>

<p>We can use <code>dotnet run</code> or <code>dotnet test</code> to run tests because technically they are console apps with a  bit of extra code scaffolded to create sample tests.</p>

<p>Let&#39;s try it.</p>
<div class="highlight"><pre><span></span>dotnet run
</pre></div>
<p>You should see a bunch of output and stuff that looks like errors. That&#39;s ok. Some of the tests are meant to fail as demonstrations in the sample code. The most interesting bit in the last line.</p>
<div class="highlight"><pre><span></span>20:56:42 INF] EXPECTO! 8 tests run in 00:00:00.8850460 for samples – 2 passed, 1 ignored, 5 failed, 0 errored.  &lt;Expecto
</pre></div>
<p>Here we see a report of the number of tests that failed, were ignored, and passed.</p>

<p>(wait for stickies)</p>

<p>Let&#39;s see it with <code>dotnet test</code></p>
<div class="highlight"><pre><span></span>dotnet <span class="nb">test</span>
</pre></div>
<p>We will write more tests later.</p>

<p><img title="100% code coverage you will have." alt="Imgur" src="https://i.imgur.com/ZulrmEf.jpg" /></p>

<h2 id="solution-file">Solution file</h2>

<p>Let&#39;s create a solution file so we can tie all of our projects together. The solution provides these benefits:</p>

<ul>
<li><code>dotnet build</code> all of our projects at once</li>
<li><code>dotnet test</code> all of you test projects at once</li>
<li>Visual Studio and Visual Studio Code use the solution file to organize projects</li>
</ul>

<p>Go up two levels so you are now in the <code>interactive-workshop</code> folder.</p>
<div class="highlight"><pre><span></span><span class="nb">cd</span> ../..
</pre></div>
<p>Check to make sure.</p>
<div class="highlight"><pre><span></span><span class="nb">pwd</span>
</pre></div><div class="highlight"><pre><span></span>cd
</pre></div>
<h3 id="dotnet-build-without-a-solution-file">dotnet build without a solution file</h3>

<p>Let&#39;s try building (compiling) code without a solution file.</p>
<div class="highlight"><pre><span></span>dotnet build
</pre></div>
<p>What happened? A whole lotta nothing. But that&#39;s ok because we will scaffold a solution file to help us.</p>
<div class="highlight"><pre><span></span>MSBUILD : error MSB1003: Specify a project or solution file. The current working directory does not contain a project or solution file.
</pre></div>
<p>Now use <code>dotnet new</code> to create the solution file.</p>
<div class="highlight"><pre><span></span>dotnet new sln
</pre></div>
<p>Let&#39;s look inside it to see what happened.</p>
<div class="highlight"><pre><span></span>ls -la
</pre></div><div class="highlight"><pre><span></span>dir
</pre></div>
<p>Notice that <code>dotnet new</code> created a solution file with the same name as the folder.</p>

<p>Did you see this file?</p>
<div class="highlight"><pre><span></span>interactive-workshop.sln
</pre></div>
<p>Let&#39;s see what is inside.</p>
<div class="highlight"><pre><span></span>cat interactive-workshop.sln
</pre></div><div class="highlight"><pre><span></span>type interactive-workshop.sln
</pre></div>
<blockquote>
<p>Pro tip: tab complete will save you time and money!</p>
</blockquote>

<p>That&#39;s a lot of weird stuff. It doesn&#39;t matter much but it&#39;s mostly a bunch of stuff <code>msbuild</code> and Visual Studio understand.</p>

<p>Notice that there are no references to any of our workshop projects. That&#39;s ok. We are going to add them.</p>

<p>But first try a <code>dotnet build</code> to see what happens.</p>

<p><code>dotnet</code> doesn&#39;t know what to build. That&#39;s ok. We are going to help it.</p>

<p>(wait for stickies)</p>

<h3 id="dotnet-sln-add">dotnet sln add</h3>

<p>We have a new command to use that helps us with solution files. Let&#39;s see what it does.</p>
<div class="highlight"><pre><span></span>dotnet sln -h
</pre></div><div class="highlight"><pre><span></span>Commands:
  add &lt;PROJECT_PATH&gt;      Add one or more projects to a solution file.
  list                    List all projects in a solution file.
  remove &lt;PROJECT_PATH&gt;   Remove one or more projects from a solution file.
</pre></div>
<p>Cool! Looks like we can add projects, list projects, and remove projects.</p>

<p>Let&#39;s add the <code>workshop.domain</code> project.</p>
<div class="highlight"><pre><span></span>dotnet sln add src/workshop.domain
</pre></div>
<p>If that worked you should be able to build now.</p>
<div class="highlight"><pre><span></span>dotnet build
</pre></div>
<p>What happened?</p>

<p>Did it look a little like this?</p>
<div class="highlight"><pre><span></span>Microsoft (R) Build Engine version 15.9.20+g88f5fadfbe for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restoring packages for /mnt/c/Users/Marnee/interactive-workshop/src/workshop.domain/workshop.domain.fsproj...
  Generating MSBuild file /mnt/c/Users/Marnee/interactive-workshop/src/workshop.domain/obj/workshop.domain.fsproj.nuget.g.props.
  Generating MSBuild file /mnt/c/Users/Marnee/interactive-workshop/src/workshop.domain/obj/workshop.domain.fsproj.nuget.g.targets.
  Restore completed in 541.07 ms for /mnt/c/Users/Marnee/interactive-workshop/src/workshop.domain/workshop.domain.fsproj.
  workshop.domain -&gt; /mnt/c/Users/Marnee/interactive-workshop/src/workshop.domain/bin/Debug/netstandard2.0/workshop.domain.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:11.74
</pre></div>
<p>Great! That worked. Now try to add the <code>.cli</code> and <code>.test</code> projects.</p>
<div class="highlight"><pre><span></span>dotnet sln add src/workshop.cli
dotnet sln add src/workshop.test
</pre></div>
<p>(wait for stickies)</p>

<p>What happens when you <code>dotnet build</code> now?</p>

<p>(wait for stickies)</p>

<p>Awesome! This will save us time and typing effort and lessen our cognitive burden.</p>

<p><img title="Great success!" alt="solution files" src="https://i.imgur.com/vAypKi8.jpg" /></p>

<h2 id="domain-model-and-domain-logic">Domain model and domain logic</h2>

<p>Get yourself to the <code>workshop.domain</code> folder. Let&#39;s code our domain with a little F#.</p>
<div class="highlight"><pre><span></span><span class="nb">cd</span> src/workshop.domain
</pre></div>
<h3 id="the-domain">The domain</h3>

<p>We work at a University so let&#39;s model a course.</p>
<div class="highlight"><pre><span></span>Field         Type      Constraints

Number        int       5 digits
Name          string    100 chars
Description   string    500 chars
Credits       int       less than 4
Department    int       must be a valid department code
</pre></div>
<p>Ok that&#39;s good enough to get started.</p>

<p>With your favorite editor open the Library.fs file.</p>

<p>Let&#39;s model the <code>Department</code> first. For this we will use a <code>discriminated union</code>.</p>

<p>It looks like this and you can think of it like an enum.</p>
<div class="highlight"><pre><span></span><span class="k">module</span> <span class="nn">Workshop</span> <span class="o">=</span>
    <span class="k">type</span> <span class="nc">DepartmentCode</span> <span class="o">=</span>
        <span class="o">|</span> <span class="n">Engineering</span>
        <span class="o">|</span> <span class="n">Geosciences</span>
        <span class="o">|</span> <span class="n">FineArts</span>
</pre></div>
<p>The department can be for one of three departments:<br>
* Engineering<br>
* Geosciences<br>
* FineArts</p>

<p>Each department has a department code. Let&#39;s add a way to get the department code from the DepartmentCode type.</p>
<div class="highlight"><pre><span></span><span class="k">module</span> <span class="nn">Workshop</span> <span class="o">=</span>
    <span class="k">type</span> <span class="nc">Department</span> <span class="o">=</span> 
        <span class="o">|</span> <span class="n">Engineering</span> 
        <span class="o">|</span> <span class="n">Geosciences</span>
        <span class="o">|</span> <span class="n">FineArts</span>
        <span class="o">|</span> <span class="n">NotFound</span>
        <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToCode</span><span class="bp">()</span> <span class="o">=</span>
            <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
            <span class="o">|</span> <span class="n">Engineering</span>   <span class="o">-&gt;</span> <span class="mi">100</span>
            <span class="o">|</span> <span class="n">Geosciences</span>   <span class="o">-&gt;</span> <span class="mi">200</span>
            <span class="o">|</span> <span class="n">FineArts</span>      <span class="o">-&gt;</span> <span class="mi">300</span>
            <span class="o">|</span> <span class="n">NotFound</span>      <span class="o">-&gt;</span> <span class="mi">0</span>
        <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToString</span><span class="bp">()</span> <span class="o">=</span>
            <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
            <span class="o">|</span> <span class="n">Engineering</span>   <span class="o">-&gt;</span> <span class="s">&quot;Engineering&quot;</span>
            <span class="o">|</span> <span class="n">Geosciences</span>   <span class="o">-&gt;</span> <span class="s">&quot;Geosciences&quot;</span>
            <span class="o">|</span> <span class="n">FineArts</span>      <span class="o">-&gt;</span> <span class="s">&quot;Fine Arts&quot;</span>
            <span class="o">|</span> <span class="o">_</span>             <span class="o">-&gt;</span> <span class="nn">String</span><span class="p">.</span><span class="n">Empty</span>
</pre></div>
<p>Let&#39;s add some more. Remember the domain?</p>
<div class="highlight"><pre><span></span>Field         Type      Constraints

Number        int       5 digits
Name          string    100 chars
Description   string    500 chars
Credits       int       less than 4
Department    int       must be a valid department code
</pre></div>
<p>Let&#39;s create a type that models everything that makes up a course. We will use a <code>Record Type</code> to represent a course.</p>

<blockquote>
<p>Pro tip: copy and paste! Don&#39;t type this all.</p>
</blockquote>
<div class="highlight"><pre><span></span>    <span class="k">type</span> <span class="nc">Course</span> <span class="o">=</span>
        <span class="o">{</span>
            <span class="n">Number</span>      <span class="o">:</span> <span class="n">int</span>
            <span class="n">Name</span>        <span class="o">:</span> <span class="n">CourseName</span>
            <span class="n">Description</span> <span class="o">:</span> <span class="kt">string</span>
            <span class="n">Credits</span>     <span class="o">:</span> <span class="n">int</span>
            <span class="n">Department</span>  <span class="o">:</span> <span class="n">Department</span>
        <span class="o">}</span>
</pre></div>
<p>Record types define the shape of your data. You can think of them like properties on a class.</p>

<p>Let&#39;s make sure you don&#39;t have any syntax errors. Let&#39;s run a build. Remember how to do that?</p>
<div class="highlight"><pre><span></span>dotnet build
</pre></div>
<p>Did you get any errors? Try to fix them. I&#39;ll help.</p>

<p>(wait for stickies)</p>

<p>The code should currently look ike this:</p>
<div class="highlight"><pre><span></span><span class="k">namespace</span> <span class="n">workshop</span><span class="o">.</span><span class="n">domain</span>

<span class="k">open</span> <span class="nn">System</span>

<span class="k">module</span> <span class="nn">Say</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">hello</span> <span class="n">name</span> <span class="o">=</span>
        <span class="n">printfn</span> <span class="s">&quot;Hello %s&quot;</span> <span class="n">name</span>

<span class="k">module</span> <span class="nn">Workshop</span> <span class="o">=</span>
    <span class="k">type</span> <span class="nc">Department</span> <span class="o">=</span> 
        <span class="o">|</span> <span class="n">Engineering</span> 
        <span class="o">|</span> <span class="n">Geosciences</span>
        <span class="o">|</span> <span class="n">FineArts</span>
        <span class="o">|</span> <span class="n">NotFound</span>
        <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToCode</span><span class="bp">()</span> <span class="o">=</span>
            <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
            <span class="o">|</span> <span class="n">Engineering</span>   <span class="o">-&gt;</span> <span class="mi">100</span>
            <span class="o">|</span> <span class="n">Geosciences</span>   <span class="o">-&gt;</span> <span class="mi">200</span>
            <span class="o">|</span> <span class="n">FineArts</span>      <span class="o">-&gt;</span> <span class="mi">300</span>
            <span class="o">|</span> <span class="n">NotFound</span>      <span class="o">-&gt;</span> <span class="mi">0</span>
        <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToString</span><span class="bp">()</span> <span class="o">=</span>
            <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
            <span class="o">|</span> <span class="n">Engineering</span>   <span class="o">-&gt;</span> <span class="s">&quot;Engineering&quot;</span>
            <span class="o">|</span> <span class="n">Geosciences</span>   <span class="o">-&gt;</span> <span class="s">&quot;Geosciences&quot;</span>
            <span class="o">|</span> <span class="n">FineArts</span>      <span class="o">-&gt;</span> <span class="s">&quot;Fine Arts&quot;</span>
            <span class="o">|</span> <span class="o">_</span>             <span class="o">-&gt;</span> <span class="nn">String</span><span class="p">.</span><span class="n">Empty</span>

    <span class="k">let</span> <span class="nv">getDepartment</span> <span class="n">code</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">code</span> <span class="k">with</span>
        <span class="o">|</span> <span class="mi">100</span>   <span class="o">-&gt;</span> <span class="n">Engineering</span>
        <span class="o">|</span> <span class="mi">200</span>   <span class="o">-&gt;</span> <span class="n">Geosciences</span>
        <span class="o">|</span> <span class="mi">300</span>   <span class="o">-&gt;</span> <span class="n">FineArts</span>
        <span class="o">|</span> <span class="o">_</span>     <span class="o">-&gt;</span> <span class="n">NotFound</span>

    <span class="k">type</span> <span class="nc">Course</span> <span class="o">=</span>
        <span class="o">{</span>
            <span class="n">Number</span>      <span class="o">:</span> <span class="n">int</span>
            <span class="n">Name</span>        <span class="o">:</span> <span class="kt">string</span>
            <span class="n">Description</span> <span class="o">:</span> <span class="kt">string</span>
            <span class="n">Credits</span>     <span class="o">:</span> <span class="n">int</span>
            <span class="n">Department</span>  <span class="o">:</span> <span class="n">Department</span>
        <span class="o">}</span>
</pre></div>
<p>That&#39;s nice. Let&#39;s code some constraints. Let&#39;s look at the domain again.</p>
<div class="highlight"><pre><span></span>Field         Type      Constraints

Number        int       5 digits, less than 100000
Name          string    100 chars
</pre></div>
<p>Let&#39;s do <code>Name</code> first.</p>

<p>Copy and paste this above <code>type Course</code>, and I will explain it.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">CourseName</span> <span class="o">=</span> <span class="k">private</span> <span class="n">CourseName</span> <span class="k">of</span> <span class="kt">string</span>
<span class="k">module</span> <span class="nn">CourseName</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">create</span> <span class="o">(</span><span class="n">s</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">s</span><span class="o">.</span><span class="n">Trim</span><span class="bp">()</span> <span class="k">with</span>
        <span class="o">|</span> <span class="n">nm</span> <span class="k">when</span> <span class="n">nm</span><span class="o">.</span><span class="n">Length</span> <span class="o">&lt;=</span> <span class="mi">100</span>  <span class="o">-&gt;</span> <span class="n">CourseName</span> <span class="n">nm</span>
        <span class="o">|</span> <span class="n">nm</span>                        <span class="o">-&gt;</span> <span class="n">CourseName</span> <span class="o">(</span><span class="n">nm</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">100</span><span class="o">))</span>
    <span class="k">let</span> <span class="nv">value</span> <span class="o">(</span><span class="n">CourseName</span> <span class="n">s</span><span class="o">)</span> <span class="o">=</span> <span class="n">s</span>
</pre></div>
<p>This makes it so that you can <strong>only</strong> create a CourseName type things through the create function.</p>

<p>Try to build to check for errors:</p>
<div class="highlight"><pre><span></span>dotnet build
</pre></div>
<p>(wait for stickies)</p>

<p>Now that we have a <code>CourseName</code> type we can make the Name field in course that type. Like this.</p>
<div class="highlight"><pre><span></span><span class="k">type</span> <span class="nc">Course</span> <span class="o">=</span>
    <span class="o">{</span>
        <span class="n">Number</span>      <span class="o">:</span> <span class="n">int</span>
        <span class="n">Name</span>        <span class="o">:</span> <span class="n">CourseName</span>
        <span class="n">Description</span> <span class="o">:</span> <span class="kt">string</span>
        <span class="n">Credits</span>     <span class="o">:</span> <span class="n">int</span>
        <span class="n">Department</span>  <span class="o">:</span> <span class="n">Department</span>
    <span class="o">}</span>
</pre></div>
<p>(wait for stickies)</p>

<p>This means that for every instance of a Course type, you will only be able to set the Name to a value that passes the CourseName constraints. Like this.</p>
<div class="highlight"><pre><span></span><span class="n">Name</span> <span class="o">=</span> <span class="nn">CourseName</span><span class="p">.</span><span class="n">create</span> <span class="s">&quot;Underwater Basket Weaving&quot;</span>
</pre></div>
<p>Create a testCourse like this.</p>
<div class="highlight"><pre><span></span><span class="k">let</span> <span class="nv">testCourse</span> <span class="o">=</span>
  <span class="o">{</span>
      <span class="n">Number</span> <span class="o">=</span> <span class="mi">9999</span>
      <span class="n">Name</span> <span class="o">=</span> <span class="nn">CourseName</span><span class="p">.</span><span class="n">create</span> <span class="s">&quot;Underwater Basket Weaving&quot;</span>
      <span class="n">Description</span> <span class="o">=</span> <span class="s">&quot;Traditional basket weaving done under water for best effect.&quot;</span>
      <span class="n">Credits</span> <span class="o">=</span> <span class="mi">3</span>
      <span class="n">Department</span> <span class="o">=</span> <span class="n">FineArts</span>
  <span class="o">}</span>
</pre></div>
<p>The whole file.</p>
<div class="highlight"><pre><span></span><span class="k">namespace</span> <span class="n">workshop</span><span class="o">.</span><span class="n">domain</span>

<span class="k">open</span> <span class="nn">System</span>

<span class="k">module</span> <span class="nn">Say</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">hello</span> <span class="n">name</span> <span class="o">=</span>
        <span class="n">printfn</span> <span class="s">&quot;Hello %s&quot;</span> <span class="n">name</span>

<span class="k">module</span> <span class="nn">Workshop</span> <span class="o">=</span>
    <span class="k">type</span> <span class="nc">Department</span> <span class="o">=</span> 
        <span class="o">|</span> <span class="n">Engineering</span> 
        <span class="o">|</span> <span class="n">Geosciences</span>
        <span class="o">|</span> <span class="n">FineArts</span>
        <span class="o">|</span> <span class="n">NotFound</span>
        <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToCode</span><span class="bp">()</span> <span class="o">=</span>
            <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
            <span class="o">|</span> <span class="n">Engineering</span>   <span class="o">-&gt;</span> <span class="mi">100</span>
            <span class="o">|</span> <span class="n">Geosciences</span>   <span class="o">-&gt;</span> <span class="mi">200</span>
            <span class="o">|</span> <span class="n">FineArts</span>      <span class="o">-&gt;</span> <span class="mi">300</span>
            <span class="o">|</span> <span class="n">NotFound</span>      <span class="o">-&gt;</span> <span class="mi">0</span>
        <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="nf">ToString</span><span class="bp">()</span> <span class="o">=</span>
            <span class="k">match</span> <span class="n">this</span> <span class="k">with</span>
            <span class="o">|</span> <span class="n">Engineering</span>   <span class="o">-&gt;</span> <span class="s">&quot;Engineering&quot;</span>
            <span class="o">|</span> <span class="n">Geosciences</span>   <span class="o">-&gt;</span> <span class="s">&quot;Geosciences&quot;</span>
            <span class="o">|</span> <span class="n">FineArts</span>      <span class="o">-&gt;</span> <span class="s">&quot;Fine Arts&quot;</span>
            <span class="o">|</span> <span class="o">_</span>             <span class="o">-&gt;</span> <span class="nn">String</span><span class="p">.</span><span class="n">Empty</span>

    <span class="k">let</span> <span class="nv">getDepartment</span> <span class="n">code</span> <span class="o">=</span>
        <span class="k">match</span> <span class="n">code</span> <span class="k">with</span>
        <span class="o">|</span> <span class="mi">100</span>   <span class="o">-&gt;</span> <span class="n">Engineering</span>
        <span class="o">|</span> <span class="mi">200</span>   <span class="o">-&gt;</span> <span class="n">Geosciences</span>
        <span class="o">|</span> <span class="mi">300</span>   <span class="o">-&gt;</span> <span class="n">FineArts</span>
        <span class="o">|</span> <span class="o">_</span>     <span class="o">-&gt;</span> <span class="n">NotFound</span>

    <span class="k">type</span> <span class="nc">CourseName</span> <span class="o">=</span> <span class="k">private</span> <span class="n">CourseName</span> <span class="k">of</span> <span class="kt">string</span>
    <span class="k">module</span> <span class="nn">CourseName</span> <span class="o">=</span>
        <span class="k">let</span> <span class="nv">create</span> <span class="o">(</span><span class="n">s</span><span class="o">:</span><span class="kt">string</span><span class="o">)</span> <span class="o">=</span>
            <span class="k">match</span> <span class="n">s</span><span class="o">.</span><span class="n">Trim</span><span class="bp">()</span> <span class="k">with</span>
            <span class="o">|</span> <span class="n">nm</span> <span class="k">when</span> <span class="n">nm</span><span class="o">.</span><span class="n">Length</span> <span class="o">&lt;=</span> <span class="mi">100</span>  <span class="o">-&gt;</span> <span class="n">CourseName</span> <span class="n">nm</span>
            <span class="o">|</span> <span class="n">nm</span>                        <span class="o">-&gt;</span> <span class="n">CourseName</span> <span class="o">(</span><span class="n">nm</span><span class="o">.</span><span class="n">Substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="mi">100</span><span class="o">))</span>
        <span class="k">let</span> <span class="nv">value</span> <span class="o">(</span><span class="n">CourseName</span> <span class="n">s</span><span class="o">)</span> <span class="o">=</span> <span class="n">s</span>   

    <span class="k">type</span> <span class="nc">Course</span> <span class="o">=</span>
        <span class="o">{</span>
            <span class="n">Number</span>      <span class="o">:</span> <span class="n">int</span>
            <span class="n">Name</span>        <span class="o">:</span> <span class="n">CourseName</span>
            <span class="n">Description</span> <span class="o">:</span> <span class="kt">string</span>
            <span class="n">Credits</span>     <span class="o">:</span> <span class="n">int</span>
            <span class="n">Department</span>  <span class="o">:</span> <span class="n">Department</span>
        <span class="o">}</span>

    <span class="k">let</span> <span class="nv">testCourse</span> <span class="o">=</span>
      <span class="o">{</span>
          <span class="n">Number</span> <span class="o">=</span> <span class="mi">9999</span>
          <span class="n">Name</span> <span class="o">=</span> <span class="nn">CourseName</span><span class="p">.</span><span class="n">create</span> <span class="s">&quot;Underwater Basket Weaving&quot;</span>
          <span class="n">Description</span> <span class="o">=</span> <span class="s">&quot;Traditional basket weaving done under water for best effect.&quot;</span>
          <span class="n">Credits</span> <span class="o">=</span> <span class="mi">3</span>
          <span class="n">Department</span> <span class="o">=</span> <span class="n">FineArts</span>
      <span class="o">}</span>
</pre></div>
<p>Let&#39;s see if your code builds. Do you remember how to do that?</p>
<div class="highlight"><pre><span></span>dotnet build
</pre></div>
<p>(wait for stickies)</p>

<p><img title="DSL plus contraints. Make impossible states impossible and easy to read." alt="Remember" src="https://i.imgur.com/xqp7Yoh.jpg" /></p>

<h2 id="write-tests-against-your-domain-code">Write tests against your domain code</h2>

<p>Now that we have some code we can write tests against it.</p>

<p>First, we will need to reference the domain project in the test project so we can use that code.</p>

<blockquote>
<p>Pro tip: We can do everything by path, so we don&#39;t have to change directories. </p>
</blockquote>

<p>Remember the usage?</p>
<div class="highlight"><pre><span></span>Usage: dotnet add <span class="o">[</span>options<span class="o">]</span> &lt;PROJECT&gt; <span class="o">[</span>command<span class="o">]</span>
Commands:
  package &lt;PACKAGE_NAME&gt;     Add a NuGet package reference to the project.
  reference &lt;PROJECT_PATH&gt;   Add a project-to-project reference to the project.
</pre></div>
<p>First go to the top level folder:</p>

<p>Bash/Terminal<br>
<code>bash<br>
cd ../..<br>
</code></p>

<p>DoS<br>
<code><br>
cd ..\..<br>
</code></p>

<p>Do it like this.</p>
<div class="highlight"><pre><span></span>dotnet add src/workshop.test reference src/workshop.domain
</pre></div>
<p>Now let&#39;s check the <code>proj</code> file.</p>

<p>BaSH/Terminal</p>
<div class="highlight"><pre><span></span>cat src/workshop.test/workshop.test.fsproj
</pre></div>
<p>DoS</p>
<div class="highlight"><pre><span></span>type src\workshop.test\workshop.test.fsproj
</pre></div>
<blockquote>
<p>Pro tip: If you aren&#39;t using tab complete then you are doing it wrong.</p>
</blockquote>

<p>Did you see this?</p>
<div class="highlight"><pre><span></span><span class="nt">&lt;ItemGroup&gt;</span>
    <span class="nt">&lt;ProjectReference</span> <span class="na">Include=</span><span class="s">&quot;..\workshop.domain\workshop.domain.fsproj&quot;</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/ItemGroup&gt;</span>
</pre></div>
<p>(wait for stickies)</p>

<p>Great. Now let&#39;s write a test.</p>

<p>Create a new file and open it in your editor. I will use vim and Visual Studio Code.</p>
<div class="highlight"><pre><span></span>vim src/workshop.test/DomainTests.fs
</pre></div><div class="highlight"><pre><span></span>code src/workshop.test/DomainTests.fs
</pre></div>
<p>Add the code:</p>
<div class="highlight"><pre><span></span><span class="k">module</span> <span class="nn">DomainTests</span>

<span class="k">open</span> <span class="nn">Expecto</span>
<span class="k">open</span> <span class="nn">workshop.domain</span>

<span class="o">[&lt;</span><span class="n">Tests</span><span class="o">&gt;]</span>
<span class="k">let</span> <span class="nv">tests</span> <span class="o">=</span>
  <span class="n">testList</span> <span class="s">&quot;Course Tests&quot;</span> <span class="o">[</span>
      <span class="n">testCase</span> <span class="s">&quot;Engineering convert to code 100&quot;</span> <span class="o">&lt;|</span> <span class="k">fun</span> <span class="o">_</span> <span class="o">-&gt;</span>
        <span class="nn">Expect</span><span class="p">.</span><span class="n">equal</span> <span class="o">(</span><span class="nn">Workshop</span><span class="p">.</span><span class="nn">Engineering</span><span class="p">.</span><span class="n">ToCode</span><span class="bp">()</span><span class="o">)</span> <span class="mi">100</span> <span class="s">&quot;Engineering course code should be 100&quot;</span>
  <span class="o">]</span>
</pre></div>
<blockquote>
<p>Pro tip: You can copy and paste. Don&#39;t type it all in.</p>
</blockquote>

<p>Save the file.</p>

<p>Now we need to add the file to the project file. This is so the compiler knows what to compile. </p>

<blockquote>
<p>In F# the order of the files matters. The proj file specifies the order of the files.</p>
</blockquote>

<p>Open <code>workshop.test.fsproj</code>. Add the file in the right order. Also, let&#39;s remove the <code>Samples.fs</code> file to keep it simple.</p>
<div class="highlight"><pre><span></span><span class="nt">&lt;ItemGroup&gt;</span>
  <span class="nt">&lt;Compile</span> <span class="na">Include=</span><span class="s">&quot;DomainTests.fs&quot;</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;Compile</span> <span class="na">Include=</span><span class="s">&quot;Main.fs&quot;</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/ItemGroup&gt;</span>
</pre></div>
<p>Let&#39;s check the code by building the test project. Let&#39;s take the easy way and just build the solution.</p>
<div class="highlight"><pre><span></span>dotnet build
</pre></div>
<p>(wait for stickies)</p>

<p>If it is building, let&#39;s run the tests.</p>
<div class="highlight"><pre><span></span>dotnet <span class="nb">test</span>
</pre></div>
<p>Did you notice that we don&#39;t need to specify a test project? <code>dotnet</code> will iterate through each project in the solution file looking for a test project. When it finds one it will try to run the tests.</p>
<div class="highlight"><pre><span></span>Build started, please wait...
Skipping running test for project /mnt/c/Users/Marnee/interactive-workshop/src/workshop.cli/workshop.cli.fsproj. To run tests with dotnet test add &quot;&lt;IsTestProject&gt;true&lt;IsTestProject&gt;&quot; property to project file.
Skipping running test for project /mnt/c/Users/Marnee/interactive-workshop/src/workshop.domain/workshop.domain.fsproj. To run tests with dotnet test add &quot;&lt;IsTestProject&gt;true&lt;IsTestProject&gt;&quot; property to project file.
Build completed.

Test run for /mnt/c/Users/Marnee/interactive-workshop/src/workshop.test/bin/Debug/netcoreapp2.2/workshop.test.dll(.NETCoreApp,Version=v2.2)
Microsoft (R) Test Execution Command Line Tool Version 15.9.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
</pre></div>
<p>And then the results of the test.</p>
<div class="highlight"><pre><span></span>Total tests: <span class="m">1</span>. Passed: <span class="m">1</span>. Failed: <span class="m">0</span>. Skipped: <span class="m">0</span>.
</pre></div>
<ul>
<li>Total tests : 1</li>
<li>Passed: 1</li>
<li>Failed: 0</li>
<li>Skipped: 0</li>
</ul>

<p>This looks good. We had one test and it passed. Yay!</p>

<p>(wait for stickies)</p>

<p><img title="Automated unit tests and very nice" alt="very nice" src="https://i.imgur.com/UFVza3d.jpg" /></p>

<p>If we have time I&#39;ll take us through writing another test and talk about Expecto.</p>

<h3 id="dotnet-watch">dotnet watch</h3>

<p>Wouldn&#39;t it be cool if we could automatically make the tests run whenever any code changes? You can!</p>

<p>We can use <code>dotnet watch</code> to do this.</p>
<div class="highlight"><pre><span></span>dotnet watch -h
</pre></div><div class="highlight"><pre><span></span>Examples:
  dotnet watch run
  dotnet watch test
</pre></div>
<p>The watch command.</p>
<div class="highlight"><pre><span></span>dotnet watch -p src/workshop.test <span class="nb">test</span>
</pre></div>
<p>The output.</p>
<div class="highlight"><pre><span></span>watch : Started
Build started, please wait...
Build completed.

Test run for /mnt/c/Users/Marnee/interactive-workshop/src/workshop.test/bin/Debug/netcoreapp2.2/workshop.test.dll(.NETCoreApp,Version=v2.2)
Microsoft (R) Test Execution Command Line Tool Version 15.9.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...

Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 25.3014 Seconds
watch : Exited
watch : Waiting for a file to change before restarting dotnet...
</pre></div>
<p>Neat!</p>

<p>Notice <code>dotnet</code>is patiently waiting for files to change.</p>
<div class="highlight"><pre><span></span>watch : Waiting for a file to change before restarting dotnet...
</pre></div>
<p>(wait for stickies)</p>

<p>Ok, now what would happen with the watched tests if I changed the domain model? Let&#39;s try it.</p>

<p>In <code>workshop.domain</code> change the </p>
<div class="highlight"><pre><span></span><span class="n">Engineering</span> <span class="n">course</span> <span class="n">code</span> <span class="k">to</span> <span class="mi">500</span> 

<span class="o">|</span> <span class="n">Engineering</span>   <span class="o">-&gt;</span> <span class="mi">500</span>
</pre></div>
<p>and then check back in your command line.</p>

<blockquote>
<p>Those of you not using a desktop, maybe you can use screen? Or just play along. I will demo.</p>
</blockquote>

<p>Your test should have failed.</p>
<div class="highlight"><pre><span></span>Failed   Course Tests/Engineering convert to code 100
Error Message:

Engineering course code should be 100.
expected: 100
  actual: 500
</pre></div>
<p>Now change the code back and see your test pass.</p>

<p><img title="Build and test without using your hands!" alt="autobuild" src="https://i.imgur.com/m3pTOWn.jpg" /></p>

<blockquote>
<p>Stop the <code>dotnet watch</code> with <code>Ctl + C</code> or <code>Ctl + D</code></p>
</blockquote>

<p>(wait for stickies)</p>

<h2 id="build-a-command-line-tool">Build a command line tool</h2>

<p>Ok we are cooking with gas! Let&#39;s build a CLI. We are going to use a package called <code>Argu</code> that will help us quickly write a command line parser.</p>

<h3 id="add-a-package-reference-to-argu">Add a package reference to Argu</h3>

<p>In order to use Argu in <code>workshop.cli</code> we will need to pull in the package.</p>

<p>First, remember how to add a dependency?</p>
<div class="highlight"><pre><span></span>dotnet add -h
</pre></div><div class="highlight"><pre><span></span>Usage: dotnet add [options] &lt;PROJECT&gt; [command]

Commands:
  package &lt;PACKAGE_NAME&gt;     Add a NuGet package reference to the project.
</pre></div><div class="highlight"><pre><span></span>dotnet add src/workshop.cli package Argu
</pre></div>
<p>This will download the package from <code>nuget</code> and add a reference in the <code>proj</code> file.</p>

<p>Did you see this?</p>
<div class="highlight"><pre><span></span>:
:
:

log  : Installing Argu 5.2.0.
info : Package &#39;Argu&#39; is compatible with all the specified frameworks in project &#39;/mnt/c/Users/Marnee/interactive-workshop/src/workshop.cli/workshop.cli.fsproj&#39;.
info : PackageReference for package &#39;Argu&#39; version &#39;5.2.0&#39; added to file &#39;/mnt/c/Users/Marnee/interactive-workshop/src/workshop.cli/workshop.cli.fsproj&#39;.
info : Committing restore...
info : Writing lock file to disk. Path: /mnt/c/Users/Marnee/interactive-workshop/src/workshop.cli/obj/project.assets.json
log  : Restore completed in 7.41 sec for /mnt/c/Users/Marnee/interactive-workshop/src/workshop.cli/workshop.cli.fsproj.
</pre></div>
<p>You&#39;re the best!</p>

<p>(wait for stickies)</p>

<p>We also need to reference <code>workshop.domain</code> so we can use it in our CLI.</p>

<p>Can you figure it out yourself?</p>
<div class="highlight"><pre><span></span>dotnet add src/workshop.test reference src/workshop.domain
</pre></div>
<p>Let&#39;s try to use it in the CLI.</p>

<p>Open <code>src/workshop.cli/Program.fs</code>.</p>

<p>Here is the code.</p>
<div class="highlight"><pre><span></span><span class="c1">// Learn more about F# at http://fsharp.org</span>

<span class="k">open</span> <span class="nn">System</span>
<span class="k">open</span> <span class="nn">Argu</span>
<span class="k">open</span> <span class="nn">workshop.domain</span>

<span class="c1">//This is where we difine what options we accept on the command line</span>
<span class="k">type</span> <span class="nc">CLIArguments</span> <span class="o">=</span>
    <span class="o">|</span> <span class="n">DepartmentCode</span> <span class="k">of</span> <span class="n">dept</span><span class="o">:</span><span class="n">int</span>
<span class="k">with</span>
    <span class="k">interface</span> <span class="n">IArgParserTemplate</span> <span class="k">with</span>
        <span class="k">member</span> <span class="n">s</span><span class="p">.</span><span class="nf">Usage</span> <span class="o">=</span>
            <span class="k">match</span> <span class="n">s</span> <span class="k">with</span>
            <span class="o">|</span> <span class="n">DepartmentCode</span> <span class="o">_</span> <span class="o">-&gt;</span> <span class="s">&quot;specify a course code.&quot;</span>

<span class="o">[&lt;</span><span class="n">EntryPoint</span><span class="o">&gt;]</span>
<span class="k">let</span> <span class="nv">main</span> <span class="n">argv</span> <span class="o">=</span>
    <span class="k">let</span> <span class="nv">errorHandler</span> <span class="o">=</span> <span class="n">ProcessExiter</span><span class="o">(</span><span class="n">colorizer</span> <span class="o">=</span> <span class="k">function</span> <span class="nn">ErrorCode</span><span class="p">.</span><span class="n">HelpText</span> <span class="o">-&gt;</span> <span class="n">None</span> <span class="o">|</span> <span class="o">_</span> <span class="o">-&gt;</span> <span class="n">Some</span> <span class="nn">ConsoleColor</span><span class="p">.</span><span class="n">Red</span><span class="o">)</span>

    <span class="k">let</span> <span class="nv">parser</span> <span class="o">=</span> <span class="nn">ArgumentParser</span><span class="p">.</span><span class="n">Create</span><span class="o">&lt;</span><span class="n">CLIArguments</span><span class="o">&gt;(</span><span class="n">programName</span> <span class="o">=</span> <span class="s">&quot;workshop&quot;</span><span class="o">,</span> <span class="n">errorHandler</span> <span class="o">=</span> <span class="n">errorHandler</span><span class="o">)</span>

    <span class="k">let</span> <span class="nv">cmd</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">ParseCommandLine</span><span class="o">(</span><span class="n">inputs</span> <span class="o">=</span> <span class="n">argv</span><span class="o">,</span> <span class="n">raiseOnUsage</span> <span class="o">=</span> <span class="k">true</span><span class="o">)</span>

    <span class="n">printfn</span> <span class="s">&quot;I&#39;m doing all the things!&quot;</span>

    <span class="k">match</span> <span class="n">cmd</span><span class="o">.</span><span class="n">TryGetResult</span><span class="o">(</span><span class="nn">CLIArguments</span><span class="p">.</span><span class="n">DepartmentCode</span><span class="o">)</span> <span class="k">with</span>
    <span class="o">|</span> <span class="n">Some</span> <span class="n">code</span> <span class="o">-&gt;</span> <span class="n">printfn</span> <span class="s">&quot;The department name is [%s]&quot;</span> <span class="o">((</span><span class="nn">Workshop</span><span class="p">.</span><span class="n">getDepartment</span> <span class="n">code</span><span class="o">).</span><span class="n">ToString</span><span class="bp">()</span><span class="o">)</span>
    <span class="o">|</span> <span class="n">None</span>      <span class="o">-&gt;</span> <span class="n">printfn</span> <span class="s">&quot;I could not understand the department code. Please see the usage.&quot;</span>


    <span class="mi">0</span> <span class="c1">// return an integer exit code</span>
</pre></div>
<p>Let&#39;s build it to check for errors:</p>
<div class="highlight"><pre><span></span>dotnet build
</pre></div>
<p>(wait for stickies)</p>

<p>Let&#39;s run it without building to save tme.</p>
<div class="highlight"><pre><span></span>dotnet run --no-build -p src/workshop.cli/
</pre></div>
<p><code>Ooops!</code> The CLI doesn&#39;t know what we want. Argu helped us write that handling in just a few lines. </p>

<p><img title="Less boilerplate means more fun" alt="testing" src="https://i.imgur.com/y4Fsz5U.jpg" /></p>

<p>Let&#39;s try that a different way. <code>dotnet</code> has a way to pass custom parameters to <code>dotnet run</code>.</p>

<p>First you have your dotnet command followed by <code>--</code> followed by the parameters.</p>

<p>What is our command usage?</p>
<div class="highlight"><pre><span></span>dotnet run --no-build -p src/workshop.cli/ -- --help
</pre></div><div class="highlight"><pre><span></span>USAGE: workshop [--help] [--departmentcode &lt;dept&gt;]

OPTIONS:

    --departmentcode &lt;dept&gt;
                          specify a course code.
    --help                display this list of options.
</pre></div>
<p>Let&#39;s try passing the department code like this.</p>
<div class="highlight"><pre><span></span>dotnet run --no-build -p src/workshop.cli/ -- --departmentcode <span class="m">100</span>
</pre></div>
<p><img title="Argu and F# FTW" alt="commandline" src="https://i.imgur.com/EwOCq50.jpg" /></p>

<p>If we have time I will show more how to use Argu.</p>

<p>(wait for stickies)</p>

<h2 id="publish-your-code-to-somewhere">Publish your code to ... somewhere</h2>

<p>Ok let&#39;s say you are ready to publish your code. You want to share the working version with the world, but you don&#39;t want users to have to run the <code>dotnet</code> command. You want them to just use your cli. You can publish your command and all of its dependencies. You can then execute the command like you would any other program. You can even put a reference in your environment or <code>/usr/bin</code>. Whatever works for you.</p>

<p>You can find out more in the Microsoft documentation <a href="https://docs.microsoft.com/en-us/dotnet/core/deploying/deploy-with-cli">Deploy with CLI</a>.</p>

<p>Let&#39;s see the usage.</p>
<div class="highlight"><pre><span></span>dotnet publish -h
</pre></div><div class="highlight"><pre><span></span>Usage: dotnet publish [options] &lt;PROJECT&gt;
</pre></div>
<p>Lots of options. Let&#39;s focus on this one for right now.</p>
<div class="highlight"><pre><span></span>-o, --output &lt;OUTPUT_DIR&gt;             The output directory to place the published artifacts in.
</pre></div>
<p>This will tell dotnet where to put your published files.</p>

<p>Let&#39;s try that. First create a publish directory.</p>
<div class="highlight"><pre><span></span>mkdir publish
</pre></div>
<p>Let&#39;s publish. Notice the <code>path</code> I put there. <code>dotnet</code> will try to create the publish file in the same directory as the project you are publishing. If we give it the relative path it will publish there, instead.</p>
<div class="highlight"><pre><span></span>dotnet publish -o ../../publish src/workshop.cli
</pre></div>
<p>(wait for stickies)</p>

<p>Check the publish folder contents.</p>

<p>BaSH/Terminal</p>
<div class="highlight"><pre><span></span>ls -la publish
</pre></div>
<p>DoS</p>
<div class="highlight"><pre><span></span>dir publish
</pre></div>
<p>We have a lot of stuff in there. </p>

<p>Let&#39;s try to run the published version.</p>

<p>BaSH/Terminal<br>
<code>bash<br>
./publish/workshop.cli.dll<br>
</code></p>

<p>DoS<br>
<code>dos<br>
publish\workshop.cli.dll<br>
</code></p>

<p>What happened? Did you get an error?</p>
<div class="highlight"><pre><span></span>Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly &#39;System.Runtime, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&#39; or one of its dependencies. The system cannot find the file specified.
</pre></div>
<p>Yep. This is because the way we published it means we need to use the dotnet command to run it. This means that if you give this to someone else to run, they will need to have dotnet installed. Let&#39;s try running it that way and then we will publish a standalone executable.</p>
<div class="highlight"><pre><span></span>dotnet publish/workshop.cli.dll
</pre></div>
<p>Did you see the output from before? Yes, because you are awesome.</p>

<p>(wait for stickies)</p>

<p>Having a dependency on <code>dotnet</code> isn&#39;t much fun, though. But that is ok because we can publish our cli as a stand alone such that all dependencies are <code>self-contained</code>. Can you guess what the option will be?</p>
<div class="highlight"><pre><span></span>dotnet publish -h
</pre></div><div class="highlight"><pre><span></span>--self-contained                      Publish the .NET Core runtime with your application so the runtime doesn&#39;t need to be installed on the target machine.
                                        The default is &#39;true&#39; if a runtime identifier is specified.
</pre></div>
<p>Let&#39;s try that.</p>
<div class="highlight"><pre><span></span>dotnet publish --self-contained -o ../../publish src/workshop.cli
</pre></div>
<p>(wait for stickies)</p>

<p><img title="The Most Interesting Man in the World uses .NET Core." alt="testing" src="https://i.imgur.com/VAb2LAA.jpg" /></p>

<p>Did you get an error? </p>
<div class="highlight"><pre><span></span>error NETSDK1031: It is not supported to build or publish a self-contained application without specifying a RuntimeIdentifier.  Please either specify a RuntimeIdentifier or set SelfContained to false. [/mnt/c/Users/Marnee/interactive-workshop/src/workshop.cli/workshop.cli.fsproj]
</pre></div>
<p>That&#39;s ok. We need to specify a runtime identifier. This is basically the environment you want to run it on.</p>

<p>This is the usage.</p>
<div class="highlight"><pre><span></span>-r, --runtime &lt;RUNTIME_IDENTIFIER&gt;    The target runtime to publish for. This is used when creating a self-contained deployment.
                                        The default is to publish a framework-dependent application.
</pre></div>
<p>Let&#39;s do that. Here are some common identifiers.</p>

<ul>
<li>win10-x64 (Windows 10)</li>
<li>linux-x64 (Most desktop distributions like CentOS, Debian, Fedora, Ubuntu and derivatives)</li>
<li>osx.10.14-x64 (MacOS Mojave)</li>
</ul>

<p>Find the entire catalog <a href="https://docs.microsoft.com/en-us/dotnet/core/rid-catalog">here</a> if I didn&#39;t list yours above.</p>
<div class="highlight"><pre><span></span>dotnet publish -r linux-x64 --self-contained -o ../../publish src/workshop.cli
</pre></div>
<p>No errors. Let&#39;s see what is inside the publish folder.</p>
<div class="highlight"><pre><span></span>ls -la publish
</pre></div><div class="highlight"><pre><span></span>dir publish
</pre></div>
<p>That&#39;s a lot more stuff than we had before. Do you see</p>
<div class="highlight"><pre><span></span>workshop.cli.*
</pre></div>
<p>That is your &quot;executeable&quot; program.</p>

<p>(wait for stickies)</p>

<p>What happens if we try to run it?</p>
<div class="highlight"><pre><span></span> ./publish/workshop.cli
</pre></div>
<p>That looks familiar. Let&#39;s give it a department code.</p>
<div class="highlight"><pre><span></span> ./publish/workshop.cli --departmentcode <span class="m">100</span>
</pre></div>
<p>It works!</p>

<p>(wait for stickies)</p>

<p><img title="Good Guy Greg uses .NET core" alt="testing" src="https://i.imgur.com/Nwg6xRh.jpg" /></p>

<p>If we have time I will show how to create a web application using Saturn.</p>

<h2 id="web-application-safe-stack">Web Application -- SAFE STACK</h2>

<p>You&#39;ll need to install the following pre-requisites in order to build SAFE applications</p>

<ul>
<li><code>dotnet tool install -g fake-cli</code></li>
<li><code>dotnet tool install -g paket</code></li>
<li>node.js (&gt;= 8.0)</li>
<li>yarn (&gt;= 1.10.1) or npm</li>
</ul>

<h3 id="install-tools">Install tools</h3>

<h4 id="fake">FAKE</h4>
<div class="highlight"><pre><span></span>dotnet tool install -g fake-cli
</pre></div>
<h4 id="paket-package-manager">paket (package manager)</h4>
<div class="highlight"><pre><span></span>dotnet tool install -g paket
</pre></div>
<h4 id="node">Node</h4>

<p>Find your install method <a href="https://nodejs.org/en/download/package-manager/">here</a></p>

<p>Ubuntu<br>
<code>bash<br>
curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash -<br>
sudo apt-get install -y nodejs<br>
</code></p>

<h4 id="yarn">Yarn</h4>

<p>Find your install instructions <a href="https://yarnpkg.com/lang/en/docs/install/#windows-stable">here</a>.</p>

<p>Ubuntu example<br>
<code>bash<br>
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -<br>
echo &quot;deb https://dl.yarnpkg.com/debian/ stable main&quot; | sudo tee /etc/apt/sources.list.d/yarn.list<br>
</code></p>

<h3 id="scaffold-safe-stack-app">Scaffold SAFE stack app</h3>

<h4 id="install-the-template">Install the template</h4>
<div class="highlight"><pre><span></span>dotnet new -i SAFE.Template
</pre></div>
<h4 id="create-the-project">Create the project</h4>
<div class="highlight"><pre><span></span><span class="nb">cd</span> workshop.web
</pre></div><div class="highlight"><pre><span></span>dotnet new SAFE -lang F#
</pre></div>
<h3 id="build-and-run-using-fake">Build and run using FAKE</h3>

<p>If you don&#39;t have a browser this might not work so good. If it works you should see a browser window or tab appear.</p>
<div class="highlight"><pre><span></span>fake build --target run
</pre></div>
<blockquote>
<p>If you are using WSL, if you run the app you can access it from the Windows side with this URL:</p>
</blockquote>
<div class="highlight"><pre><span></span>http://localhost:8080/
</pre></div>
<p>And there ya go. A fully functional web application in only a handful of steps.</p>

<h2 id="open-the-build-script-and-walk-through-it">Open the build script and walk through it</h2>

<h2 id="the-dotnet-goat-path">The dotnet goat path</h2>

<p><img title="Goat path to glory." alt="goats" src="https://proxy.duckduckgo.com/iu/?u=http%3A%2F%2Fwww.quickmeme.com%2Fimg%2F46%2F46e64a12f117453efe8705526c25c467709cd30198aeaf592cfb76dc03d5a350.jpg&amp;f=1" /></p>

<p>Review the templates.</p>
<div class="highlight"><pre><span></span>dotnet new --list
</pre></div>
<p>Create your folder structure. Remember to be inside the folder where you want to create the project before creating the project.</p>

<blockquote>
<p>By default, dotnet new will create a project with the same name as the folder you are in. There is an option to specify the project name (<code>-n, --name</code>), which also creates the folder. I like to create the folder ahead of time so I can work out the structure first.</p>
</blockquote>
<div class="highlight"><pre><span></span>dotnet new console -lang F#
</pre></div>
<p>This created a console app.</p>

<h3 id="build-the-console-app">Build the console app</h3>
<div class="highlight"><pre><span></span>dotnet build &lt;PATH TO CONSOLE PROJECT FOLDER&gt;
</pre></div>
<h3 id="create-a-class-library-inside-the-class-library-folder-you-created">Create a class library inside the class library folder you created.</h3>
<div class="highlight"><pre><span></span>dotnet new classlib -lang F#
</pre></div>
<h3 id="build-the-class-library">Build the class library.</h3>
<div class="highlight"><pre><span></span>dotnet build &lt;PATH TO LIBRARY PROJECT FOLDER&gt;
</pre></div>
<h3 id="create-a-test-project-inside-the-test-project-folder-you-created">Create a test project inside the test project folder you created.</h3>

<p>If you want to use Expecto, you need to install the templates first.</p>
<div class="highlight"><pre><span></span>dotnet new -i Expecto.Template::*
</pre></div>
<blockquote>
<p>There are <a href="https://github.com/dotnet/templating/wiki/Available-templates-for-dotnet-new">lots of templates out there</a> for all kinds of projects. </p>
</blockquote>
<div class="highlight"><pre><span></span>dotnet new expecto -lang F#
</pre></div>
<h3 id="run-tests">Run tests</h3>
<div class="highlight"><pre><span></span>dotnet <span class="nb">test</span> &lt;PATCH TO TEST PROJECT&gt;
</pre></div>
<h3 id="create-a-solution-file-to-help-build-and-test-without-specifying-a-path-to-project-folder">Create a solution file to help build and test without specifying a <code>&lt;PATH TO PROJECT FOLDER&gt;</code>.</h3>

<blockquote>
<p>Put the solution file in a folder above the source code folder like this.</p>
</blockquote>
<div class="highlight"><pre><span></span>sln
    |
    src
        workshop.cli
        workshop.domain
        workshop.test
</pre></div><div class="highlight"><pre><span></span>dotnet new sln
</pre></div>
<blockquote>
<p>By default, this will create a solution file with the same name as the containing folder. You can use the option <code>-n</code> or <code>--name</code>.</p>
</blockquote>

<h3 id="add-projects-to-the-soution-file">Add projects to the soution file</h3>
<div class="highlight"><pre><span></span>dotnet sln add &lt;PATH TO PROJECT FOLDER&gt;
</pre></div>
<h3 id="build-using-the-sln-file">Build using the sln file</h3>

<p>Make sure you are in the same folder as the soltion file. <code>dotnet</code> will look for the sln file and build everything in the file.</p>
<div class="highlight"><pre><span></span>dotnet build
</pre></div>
<h3 id="run-test-projects-using-the-sln-file">Run test projects using the sln file</h3>
<div class="highlight"><pre><span></span>dotnet <span class="nb">test</span>
</pre></div>
<blockquote>
<p>dotnet will run any test project it finds in the solution file.</p>
</blockquote>

<h3 id="run-a-cli-using-dotnet-with-arguments">Run a cli using dotnet with arguments</h3>
<div class="highlight"><pre><span></span>dotnet run -p &lt;PATH TO CONSOLE APP&gt; -- --arg value
</pre></div>
<h3 id="publish-self-contained-app-to-target-operating-system">Publish self-contained app to target operating system</h3>
<div class="highlight"><pre><span></span>dotnet publish -r &lt;Runtime IDentifier&gt; --self-contained -o &lt;PATH TO PUBLISH FOLDER&gt; &lt;PATH TO CONSOLE PROJECT&gt;
</pre></div>
<h3 id="run-the-published-app">Run the published app</h3>
<div class="highlight"><pre><span></span>.&lt;PATH TO PUBLISHED EXECUTEABLE&gt; --argu value
</pre></div>]]></content:encoded>
      </item>
  </channel>
</rss>