<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Suraj Informatics</title>
    <description>The latest articles on DEV Community by Suraj Informatics (@suraj_informatics).</description>
    <link>https://dev.to/suraj_informatics</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4147639%2F0ebf69dd-3b29-4943-ba47-01ed6763ff12.png</url>
      <title>DEV Community: Suraj Informatics</title>
      <link>https://dev.to/suraj_informatics</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/suraj_informatics"/>
    <language>en</language>
    <item>
      <title>Building the Data Flow Behind an Unmanned Weighbridge</title>
      <dc:creator>Suraj Informatics</dc:creator>
      <pubDate>Sun, 04 Oct 2026 17:15:15 +0000</pubDate>
      <link>https://dev.to/suraj_informatics/building-the-data-flow-behind-an-unmanned-weighbridge-4oec</link>
      <guid>https://dev.to/suraj_informatics/building-the-data-flow-behind-an-unmanned-weighbridge-4oec</guid>
      <description>&lt;p&gt;An unmanned weighbridge looks like a hardware project: load cells, cameras, an RFID reader, a boom barrier. In practice most of the work is software, and specifically one question: &lt;strong&gt;when is a weighment valid?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The event sequence
&lt;/h2&gt;

&lt;p&gt;A single weighment is a small state machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;VEHICLE_DETECTED&lt;/code&gt;: RFID read or number-plate match&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POSITION_OK&lt;/code&gt;: platform sensors confirm all axles are on the scale&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WEIGHT_STABLE&lt;/code&gt;: the indicator reports a stable value for N readings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EVIDENCE_CAPTURED&lt;/code&gt;: camera snapshots stored with the transaction ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TRANSACTION_CLOSED&lt;/code&gt;: slip issued, barrier opened, record sent onward&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any state fails or times out, the transaction moves to &lt;code&gt;EXCEPTION&lt;/code&gt; and a supervisor is alerted. Nothing is silently accepted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the indicator
&lt;/h2&gt;

&lt;p&gt;Most weight indicators push a continuous string over RS-232 or TCP. The terminal parses it, checks the stability flag and keeps a short rolling window. The key design rule: &lt;strong&gt;the weight value never passes through a user-editable field.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Idempotent ERP posting
&lt;/h2&gt;

&lt;p&gt;The weighment is posted to the ERP (often SAP) with the transaction ID as the key. If the network drops, the terminal retries, and the ERP rejects duplicates. This avoids the classic double goods-receipt problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Evidence storage
&lt;/h2&gt;

&lt;p&gt;Photos are stored against the transaction ID with the time stamp and gross, tare and net weights. When someone disputes a weight months later, a single lookup returns everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling exceptions
&lt;/h2&gt;

&lt;p&gt;The exception path deserves as much design as the happy path. Typical cases: unknown RFID tag, plate read with low confidence, weight outside the order tolerance, or a camera offline. Each case gets a clear rule: hold the barrier, alert a supervisor, or allow with a flag. Log the decision and who made it, so audits can follow the trail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Offline mode
&lt;/h2&gt;

&lt;p&gt;Plants lose network more often than anyone admits. The terminal should keep a local queue of transactions and evidence, continue weighing under the same rules, and sync when the link returns, using the same idempotent keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring
&lt;/h2&gt;

&lt;p&gt;Each terminal sends a heartbeat. A dashboard shows queue length, average platform time and exception counts per site, which is where truck turnaround improvements actually become visible.&lt;/p&gt;

&lt;p&gt;We wrote a non-technical overview of the same system for plant and port managers. It covers features and rollout planning: &lt;a href="https://www.surajinformatics.com/2026/10/04/intelligent-terminal-for-weighbridges/" rel="noopener noreferrer"&gt;intelligent terminal for weighbridges&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>iot</category>
      <category>automation</category>
      <category>logistics</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Validating Container Numbers from Gate OCR with the ISO 6346 Check Digit</title>
      <dc:creator>Suraj Informatics</dc:creator>
      <pubDate>Sun, 04 Oct 2026 10:16:52 +0000</pubDate>
      <link>https://dev.to/suraj_informatics/validating-container-numbers-from-gate-ocr-with-the-iso-6346-check-digit-1g8</link>
      <guid>https://dev.to/suraj_informatics/validating-container-numbers-from-gate-ocr-with-the-iso-6346-check-digit-1g8</guid>
      <description>&lt;p&gt;Container depots and terminals increasingly read container numbers with OCR cameras at the gate. OCR is fast, but it is not perfect: a smudged &lt;code&gt;8&lt;/code&gt; becomes a &lt;code&gt;B&lt;/code&gt;, a &lt;code&gt;0&lt;/code&gt; becomes an &lt;code&gt;O&lt;/code&gt;. The good news is that every shipping container number carries its own error check, defined in &lt;strong&gt;ISO 6346&lt;/strong&gt;. You can catch most misreads with a few lines of code before they reach your yard system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of a container number
&lt;/h2&gt;

&lt;p&gt;A container number like &lt;code&gt;CSQU3054383&lt;/code&gt; has four parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CSQ&lt;/code&gt; - the owner code (3 letters)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;U&lt;/code&gt; - the equipment category (&lt;code&gt;U&lt;/code&gt; for freight containers)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;305438&lt;/code&gt; - the serial number (6 digits)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3&lt;/code&gt; - the check digit&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the check digit works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Give each letter a value starting at A = 10, skipping multiples of 11 (so 11, 22 and 33 are never used). Digits keep their own value.&lt;/li&gt;
&lt;li&gt;Multiply each of the first 10 characters by 2 to the power of its position (1, 2, 4, 8 ... 512).&lt;/li&gt;
&lt;li&gt;Add the results, take the remainder after dividing by 11, then take the last digit (a remainder of 10 becomes 0).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;letter_values&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABCDEFGHIJKLMNOPQRSTUVWXYZ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
        &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;

&lt;span class="n"&gt;VALUES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;letter_values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;PATTERN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^[A-Z]{3}[UJZ][0-9]{7}$&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_digit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;VALUES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isalpha&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PATTERN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;check_digit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CSQU3054383&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# True
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CSQU3054388&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# False - OCR misread
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using it at the gate
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reject or flag&lt;/strong&gt; any OCR reading that fails the check, and ask the gate clerk to confirm only those trucks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try likely swaps&lt;/strong&gt; (&lt;code&gt;O&lt;/code&gt;/&lt;code&gt;0&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;/&lt;code&gt;8&lt;/code&gt;, &lt;code&gt;I&lt;/code&gt;/&lt;code&gt;1&lt;/code&gt;) on a failed read and accept a candidate only if exactly one passes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log both&lt;/strong&gt; the raw OCR text and the corrected number, so you can measure camera accuracy over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This small check turns OCR from "usually right" into something a yard system can trust, which is what makes truly automated gates possible at depots and terminals.&lt;/p&gt;

&lt;p&gt;For the bigger picture of how gates, weighbridges, yards and rail sidings work together at an inland depot, see this guide to the &lt;a href="https://www.surajinformatics.com/2026/10/04/inland-container-depot-icd-what-it-is-how-it-works-and-where-automation-cuts-delays/" rel="noopener noreferrer"&gt;inland container depot (ICD)&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>logistics</category>
      <category>iot</category>
      <category>automation</category>
    </item>
    <item>
      <title>Modelling Truck Flow Through a Container Terminal as Events</title>
      <dc:creator>Suraj Informatics</dc:creator>
      <pubDate>Sat, 03 Oct 2026 10:37:26 +0000</pubDate>
      <link>https://dev.to/suraj_informatics/modelling-truck-flow-through-a-container-terminal-as-events-58ie</link>
      <guid>https://dev.to/suraj_informatics/modelling-truck-flow-through-a-container-terminal-as-events-58ie</guid>
      <description>&lt;p&gt;Container terminals are a good example of a system where the interesting data is not the state but the &lt;strong&gt;events&lt;/strong&gt;. A truck enters the gate, gets weighed, picks up a box in the yard and leaves. If each of those moments is recorded as an event, almost every operational question becomes a simple query.&lt;/p&gt;

&lt;h2&gt;
  
  
  The events
&lt;/h2&gt;

&lt;p&gt;A minimal event table for the land side of a terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;truck_events&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;          &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;visit_id&lt;/span&gt;    &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;-- one truck visit&lt;/span&gt;
  &lt;span class="n"&gt;vehicle_no&lt;/span&gt;  &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- read by ANPR&lt;/span&gt;
  &lt;span class="n"&gt;container&lt;/span&gt;   &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c1"&gt;-- read by OCR, ISO 6346 format&lt;/span&gt;
  &lt;span class="n"&gt;event_type&lt;/span&gt;  &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- GATE_IN, WEIGH_1, YARD_PICK, WEIGH_2, GATE_OUT&lt;/span&gt;
  &lt;span class="k"&gt;location&lt;/span&gt;    &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c1"&gt;-- gate lane, weighbridge id, yard block&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt;  &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Questions it answers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Truck turnaround time:&lt;/strong&gt; &lt;code&gt;GATE_OUT&lt;/code&gt; minus &lt;code&gt;GATE_IN&lt;/code&gt; per &lt;code&gt;visit_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where trucks wait:&lt;/strong&gt; the gap between consecutive events in the same visit shows which stage is slow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yard hot spots:&lt;/strong&gt; count &lt;code&gt;YARD_PICK&lt;/code&gt; events per block per hour.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why automatic capture matters
&lt;/h2&gt;

&lt;p&gt;The model only works if events are recorded at the moment they happen. Manual entry adds delay and typing errors, so the timestamps drift and the analysis lies. That is why terminals pair this kind of data model with OCR and ANPR cameras at the gate and an unmanned weighbridge: the event is created by the hardware, not by a clerk.&lt;/p&gt;

&lt;p&gt;For the operational side (the quay, the yard, the gate and the KPIs terminal managers track), see this guide to &lt;a href="https://www.surajinformatics.com/2026/10/03/container-terminal-operations-how-a-modern-terminal-works-and-where-automation-cuts-delays/" rel="noopener noreferrer"&gt;container terminal operations&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>logistics</category>
      <category>iot</category>
      <category>automation</category>
      <category>database</category>
    </item>
    <item>
      <title>Computing Truck Turnaround Time from Gate Events: A Simple Data Model</title>
      <dc:creator>Suraj Informatics</dc:creator>
      <pubDate>Wed, 30 Sep 2026 12:49:11 +0000</pubDate>
      <link>https://dev.to/suraj_informatics/computing-truck-turnaround-time-from-gate-events-a-simple-data-model-2bc3</link>
      <guid>https://dev.to/suraj_informatics/computing-truck-turnaround-time-from-gate-events-a-simple-data-model-2bc3</guid>
      <description>&lt;p&gt;Plants that automate their gates with RFID or ANPR end up with a stream of events: a vehicle was seen at the gate, stood on the weighbridge, docked at bay 4, left. Turning that stream into a useful &lt;strong&gt;truck turnaround time (TAT)&lt;/strong&gt; report is a small but surprisingly fiddly data problem. Here is a simple model that works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The events
&lt;/h2&gt;

&lt;p&gt;Each reader, camera or weighbridge terminal emits an event with the same shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GateEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;      &lt;span class="c1"&gt;# one truck visit (gate pass / trip number)
&lt;/span&gt;    &lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;       &lt;span class="c1"&gt;# plate or RFID tag
&lt;/span&gt;    &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;         &lt;span class="c1"&gt;# REPORTED, GATE_IN, WEIGH_1, BAY_CALL, DOCK_IN, DOCK_OUT, WEIGH_2, GATE_OUT
&lt;/span&gt;    &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key design choice is &lt;code&gt;visit_id&lt;/code&gt;. Using the vehicle number alone breaks as soon as the same truck makes two trips in a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage durations
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ORDER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPORTED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GATE_IN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WEIGH_1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BAY_CALL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DOCK_IN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DOCK_OUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WEIGH_2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GATE_OUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stage_durations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ORDER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ORDER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPORTED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GATE_OUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TAT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GATE_OUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REPORTED&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that TAT starts at &lt;code&gt;REPORTED&lt;/code&gt;, not &lt;code&gt;GATE_IN&lt;/code&gt;. Time spent queuing outside the gate is real turnaround time, even if the gate register never sees it. An ANPR camera a little way up the approach road is the easiest way to capture it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that go wrong in real data
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Missing events.&lt;/strong&gt; A tag is not read at one stage. Report the visit as incomplete rather than guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate reads.&lt;/strong&gt; Readers fire several times as a truck passes. Keep the first read per stage per visit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clock drift.&lt;/strong&gt; Sync every device to one NTP source, or stage durations turn negative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value-adding vs waiting.&lt;/strong&gt; Only &lt;code&gt;DOCK_IN-&amp;gt;DOCK_OUT&lt;/code&gt; is loading. Everything else is waiting, and that is where the savings are.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;Once each stage has a duration, you can group by shift, product, transporter or gate and see exactly where trucks lose time. This is the approach behind the stage-wise TAT dashboards in GATIVRA, the plant logistics platform from Suraj Informatics.&lt;/p&gt;

&lt;p&gt;For the business side (what TAT means, the formula and seven ways to reduce it), see the full guide: &lt;a href="https://www.surajinformatics.com/2026/09/29/tat-in-logistics/" rel="noopener noreferrer"&gt;TAT in Logistics: What Truck Turnaround Time Means and How to Cut It&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>logistics</category>
      <category>python</category>
      <category>iot</category>
      <category>datamodeling</category>
    </item>
    <item>
      <title>How Weighbridge Software Moves Data from the Scale to Your ERP</title>
      <dc:creator>Suraj Informatics</dc:creator>
      <pubDate>Mon, 28 Sep 2026 17:03:17 +0000</pubDate>
      <link>https://dev.to/suraj_informatics/how-weighbridge-software-moves-data-from-the-scale-to-your-erp-12o3</link>
      <guid>https://dev.to/suraj_informatics/how-weighbridge-software-moves-data-from-the-scale-to-your-erp-12o3</guid>
      <description>&lt;p&gt;In plants, ports and logistics parks, the weighbridge is one of the few places where physical goods turn into data. Every truck is weighed at least twice (in and out), and those numbers end up on invoices, purchase orders and stock reports. If the data flow is manual, errors creep in fast.&lt;/p&gt;

&lt;p&gt;Here is a simple way to think about how modern weighbridge software moves data from the platform to the ERP.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Identify the vehicle
&lt;/h2&gt;

&lt;p&gt;Before any weight is recorded, the system needs to know &lt;strong&gt;which truck&lt;/strong&gt; is on the platform.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RFID&lt;/strong&gt; tags for regular transporters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ANPR&lt;/strong&gt; cameras that read number plates for vehicles without tags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The vehicle ID is matched against an expected order or gate pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Control the platform
&lt;/h2&gt;

&lt;p&gt;Traffic lights and boom barriers let only one vehicle onto the platform at a time and hold it until it is positioned correctly. The software, not an operator, decides when the vehicle may enter and leave.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Capture a stable weight
&lt;/h2&gt;

&lt;p&gt;The software reads the &lt;strong&gt;stable&lt;/strong&gt; weight from the weight indicator and stores it with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vehicle ID&lt;/li&gt;
&lt;li&gt;timestamp&lt;/li&gt;
&lt;li&gt;camera image of the truck on the platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That combination is what makes the record tamper-proof and easy to audit.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Generate the ticket
&lt;/h2&gt;

&lt;p&gt;After the second weighment, gross, tare and net weights are calculated and the weighment ticket is generated automatically. No typing, no re-keying.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Sync to ERP in real time
&lt;/h2&gt;

&lt;p&gt;The record goes to a central system and, through APIs, to the ERP or terminal management system. Purchase orders, sales orders and invoices stay in sync with actual weights, instead of waiting for end-of-day uploads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Most weighbridge disputes are not about the scale. They are about &lt;strong&gt;which vehicle was weighed, and when&lt;/strong&gt;. Linking every weighment to an automatic vehicle ID and a camera image answers that before anyone has to ask.&lt;/p&gt;




&lt;p&gt;Full guide (manual vs unmanned, features to look for, FAQ): &lt;a href="https://www.surajinformatics.com/2026/09/28/what-is-weighbridge-software/" rel="noopener noreferrer"&gt;What is weighbridge software?&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suraj Informatics: &lt;a href="https://www.surajinformatics.com" rel="noopener noreferrer"&gt;https://www.surajinformatics.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Suraj Informatics UAE: &lt;a href="https://www.surajinformatics.ae" rel="noopener noreferrer"&gt;https://www.surajinformatics.ae&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GATIVRA platform: &lt;a href="https://www.gativra.com" rel="noopener noreferrer"&gt;https://www.gativra.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>iot</category>
      <category>automation</category>
      <category>logistics</category>
      <category>erp</category>
    </item>
  </channel>
</rss>
