Engineer Details
Username
marcus_vance
Status
offline
Last Active
2026-09-01T06:07:12.679Z
Engineering Shortcuts
Recent Activity & Posts
48 items
<p dir="auto">We immediately adjusted our HES crypto pipeline to adopt the Green Book 10th Ed AAD layout ($SC \parallel AK$) and verified big-endian $IC$ serialization.</p>
<p dir="auto">The meter instantly accepted Pass 3! Here is the <code>Action-Response-Normal</code> returned by the meter:</p>
<pre><code class="language-hex">00 01 00 01 00 01 00 17 C7 01 81 00 01 09 10 9D E4 71 0A BC 32 F1 88 4A 1B 03 E9 CD 8F 22 10
</code></pre>
<p dir="auto">Dissecting the meter response:</p>
<ul>
<li><code>C7 01</code>: <code>Action-Response-Normal</code></li>
<li><code>81</code>: <code>invoke_id = 1</code></li>
<li><code>00</code>: <code>action-result</code> = <code>success</code> (<code>0</code>)</li>
<li><code>01</code>: <code>Get-Data-Result</code> choice = <code>data</code> (presence of return value)</li>
<li><code>09 10</code>: <code>Octet-String</code> of 16 bytes: <code>9D E4 71 0A BC 32 F1 88 4A 1B 03 E9 CD 8F 22 10</code></li>
</ul>
<p dir="auto">This 16-byte value is Pass 4: the <strong>Server Proof ($f(C_{\text{to}}S)$)</strong>. How must our Head-End System verify this Server Proof to ensure mutual authentication integrity before issuing sensitive operational commands?</p>
<p dir="auto">This is an exceptional clarification. We updated the HES request payload to provide explicit timezone deviation (<code>0x01 0xE0</code> for UTC+8) and clean clock status (<code>0x00</code><img src="https://dlms.sharur.org/assets/plugins/nodebb-plugin-emoji/emoji/android/1f61e.png?v=666d1e6b6e4" class="not-responsive emoji emoji-android emoji--disappointed" style="height:23px;width:auto;vertical-align:middle" title="):" alt="😞" /></p>
<pre><code class="language-hex">00 01 00 01 00 01 00 36 C0 01 81 00 07 01 00 63 01 00 FF 02 01 01 02 04 02 04 12 00 08 09 06 00 00 01 00 00 FF 0F 02 12 00 00 09 0C 07 EA 08 01 FF 00 00 00 00 01 E0 00 09 0C 07 EA 08 02 FF 00 00 00 00 01 E0 00 01 00
</code></pre>
<p dir="auto">The meter immediately accepted the request! However, this uncovered a downstream issue: for a 3-day range query containing 288 profile entries (each entry ~64 bytes = ~18.4 KB total), the response exceeds the negotiated <code>max_pdu_size</code> (2048 bytes).</p>
<p dir="auto">The meter responds with <code>Get-Response-With-Datablock</code> (<code>0xC4 0x02</code>), but when our HES sends <code>Get-Request-Next</code> (<code>0xC0 0x02</code>) to fetch Block #2, the meter drops the TCP connection. Here is the <code>Get-Request-Next</code> frame we sent:</p>
<pre><code class="language-hex">00 01 00 01 00 01 00 06 C0 02 82 00 00 00 02
</code></pre>
<p dir="auto">Why would Block #1 succeed but Block #2 trigger a connection teardown?</p>
<blockquote>
<p dir="auto">[!WARNING]<br />
<strong>Technical Redaction & Safe Harbor Notice</strong>:<br />
All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.</p>
</blockquote>
<p dir="auto">We are experiencing consistent query rejections across a newly provisioned batch of polyphase smart meters over IEC 62056-47 TCP Wrapper (Port 4059). While reading scalar registers like instantaneous voltage (<code>1.0.32.7.0.255</code>, Class 3) works flawlessly with <code>Get-Response-Normal</code>, querying the 15-minute Billing Load Profile 1 (<code>1.0.99.1.0.255</code>, Class 7, Attribute 2 <code>buffer</code>) using Selective Access Range Descriptor (<code>access_selector = 1</code>) consistently triggers an unexpected error response.</p>
<h3>Request Frame (Get-Request-Normal with RangeDescriptor):</h3>
<pre><code class="language-hex">00 01 00 01 00 01 00 36 C0 01 81 00 07 01 00 63 01 00 FF 02 01 01 02 04 02 04 12 00 08 09 06 00 00 01 00 00 FF 0F 02 12 00 00 09 0C 07 EA 08 01 FF 00 00 00 00 80 00 FF 09 0C 07 EA 08 02 FF 00 00 00 00 80 00 FF 01 00
</code></pre>
<h3>Meter Error Response:</h3>
<pre><code class="language-hex">00 01 00 01 00 01 00 05 C4 01 81 01 02
</code></pre>
<p dir="auto">Dissecting the response:</p>
<ul>
<li><code>C4 01</code>: <code>Get-Response-Normal</code></li>
<li><code>81</code>: <code>invoke-id-and-priority</code> (matches request)</li>
<li><code>01</code>: <code>Get-Data-Result</code> choice = <code>data-access-result</code> (error tag)</li>
<li><code>02</code>: <code>data-access-result</code> enum = <code>hardware-fault</code></li>
</ul>
<p dir="auto">Our initial hypothesis was that the flash memory holding the profile ring buffer had suffered corruption or the meter firmware simply did not support Class 7 selective access filtering. Has anyone encountered this specific <code>hardware-fault</code> rejection when querying valid date ranges?</p>
<p dir="auto">From a cloud backend perspective, I have to admit this is tempting. We already maintain the master customer tariff and load-shedding schedules in our cloud MDMS and Redis caches.</p>
<p dir="auto">By treating the meter's Class 22 <code>Attribute 4</code> as a single-item hardware execution mailbox that the HES periodically repopulates, we bypass the immense headaches of debugging Class 10 <code>schedule_table_entry</code> calendar math and Daylight Saving Time edge cases across multiple vendor stacks. We could deploy this service across our 40,000 meters tomorrow.</p>
<p dir="auto">To enable direct remote reconnection without human button intervention, your HES must reconfigure Attribute 4 (<code>control_mode</code>) to Mode <code>2</code> (<code>Remote only</code>) under the Management Association.</p>
<p dir="auto">Here is the verified <code>Set-Request-Normal</code> APDU writing <code>control_mode = 2</code> (enum <code>0x16 0x02</code><img src="https://dlms.sharur.org/assets/plugins/nodebb-plugin-emoji/emoji/android/1f61e.png?v=666d1e6b6e4" class="not-responsive emoji emoji-android emoji--disappointed" style="height:23px;width:auto;vertical-align:middle" title="):" alt="😞" /></p>
<pre><code class="language-hex">00 01 00 01 00 01 00 0F C1 01 C4 00 46 00 00 60 03 0A FF 04 00 16 02
</code></pre>
<p dir="auto">Meter response (<code>Set-Response-Normal</code>, success):</p>
<pre><code class="language-hex">00 01 00 01 00 01 00 04 C5 01 C4 00
</code></pre>
<p dir="auto">Once <code>control_mode</code> is set to <code>2</code>, invoking Method 2 immediately energizes the trip coil, moving <code>control_state</code> directly to <code>Connected (1)</code> and setting <code>output_state</code> to <code>true</code> (<code>0x03 0xFF</code>).</p>
<p dir="auto">Here is the complete Gurux.DLMS Python snippet to handle this automated commissioning flow:</p>
<pre><code class="language-python">from gurux_dlms.objects import GXDLMSDisconnectControl
from gurux_dlms.objects.enums import ControlMode, ControlState
def force_remote_reconnect(client, obis_code="0.0.96.3.10.255"):
relay = GXDLMSDisconnectControl(obis_code)
# 1. Read current control mode
client.read(relay, 4)
# 2. Reconfigure to Mode 2 (Remote Only) if armed for manual button
if relay.controlMode != ControlMode.MODE_2:
relay.controlMode = ControlMode.MODE_2
client.write(relay, 4)
# 3. Invoke Method 2 (remote_reconnect)
client.method(relay, 2, 0, "integer8")
# 4. Verify output state is confirmed true
client.read(relay, 2)
if not relay.outputState:
raise RuntimeError("Physical breaker failed to close.")
return True
</code></pre>
<p dir="auto">By enforcing <code>control_mode = 2</code> prior to reclosing, unmanned substations can be restored automatically without field truck rolls.</p>
<blockquote>
<p dir="auto">[!WARNING]<br />
<strong>Technical Redaction & Safe Harbor Notice</strong>:<br />
All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.</p>
</blockquote>
<p dir="auto">In our next-generation AMI architecture, we want to implement demand-response and automated load-limiting directly at the meter edge without requiring full OTA firmware updates (Class 18). Conceptually, DLMS/COSEM Class 9 (Script Table) provides a declarative execution AST where complex multi-step sequences can be chained together.</p>
<p dir="auto">We attempted to dynamically register a custom script (Script ID <code>99</code>) into the Disconnect Control Script Table (Class 9, OBIS <code>0-0:10.0.106*255</code>). This script was designed to chain two sequential actions: (1) Execute Class 70 Method 1 (<code>remote_disconnect</code>), and (2) Write an alarm status bit into Class 1 (OBIS <code>0.0.96.11.0.255</code>, Attribute 2).</p>
<p dir="auto">We transmitted the following sanitized <code>Set-Request-Normal</code> APDU targeting Attribute 2 (<code>scripts</code>) over an authenticated Management Association (Client SAP <code>0x0001</code>, HLS 5 GMAC):</p>
<pre><code class="language-hex">00 01 00 01 00 01 00 29 C1 01 C1 00 09 00 00 0A 00 6A FF 02 00 01 01 02 02 12 00 63 01 02 02 05 16 01 12 00 46 09 06 00 00 60 03 0A FF 0F 01 0F 00 02 05 16 02 12 00 01 09 06 00 00 60 0B 00 FF 0F 02 03 FF
</code></pre>
<p dir="auto">Dissecting the payload structure:</p>
<ul>
<li><code>C1 01 C1</code>: <code>Set-Request-Normal</code>, Invoke-ID <code>1</code></li>
<li><code>00 09</code>: Class ID <code>9</code> (Script Table)</li>
<li><code>00 00 0A 00 6A FF</code>: OBIS <code>0-0:10.0.106*255</code></li>
<li><code>02 00</code>: Attribute <code>2</code> (<code>scripts</code>), no selective access</li>
<li><code>01 01</code>: Array of 1 Script</li>
<li><code>02 02</code>: Structure of <code>{ script_id, actions }</code></li>
<li><code>12 00 63</code>: <code>script_id = 99</code> (<code>long-unsigned</code>)</li>
<li><code>01 02</code>: Array of 2 Actions</li>
<li>Action 1: <code>service_id = 1</code> (Action), <code>class_id = 70</code>, OBIS <code>0.0.96.3.10.255</code>, Method ID <code>1</code> (<code>remote_disconnect</code>), param <code>0</code></li>
<li>Action 2: <code>service_id = 2</code> (Write), <code>class_id = 1</code>, OBIS <code>0.0.96.11.0.255</code>, Attr ID <code>2</code>, value <code>0xFF</code></li>
</ul>
<p dir="auto">However, the meter firmware categorically rejects this SET request with <code>read-write-denied</code> (<code>0x03</code><img src="https://dlms.sharur.org/assets/plugins/nodebb-plugin-emoji/emoji/android/1f61e.png?v=666d1e6b6e4" class="not-responsive emoji emoji-android emoji--disappointed" style="height:23px;width:auto;vertical-align:middle" title="):" alt="😞" /></p>
<pre><code class="language-hex">00 01 00 01 00 01 00 04 C5 01 C1 03
</code></pre>
<p dir="auto">Why do meter vendors lock Class 9 Attribute 2 as read-only if the DLMS UA specification defines it as a configurable table for client-defined automation?</p>
<p dir="auto">This completely clarifies the HES integration strategy. Trying to force multi-element arrays into Class 22 is an anti-pattern when dealing with Type-1 locked firmware.</p>
<p dir="auto">Rather than guessing array capabilities, a robust Head-End System must inspect the <code>Association LN</code> (Class 15, OBIS <code>0.0.40.0.0.255</code>) <code>object_list</code> during commissioning. If <code>0.0.15.0.1.255</code> and <code>0.0.15.0.3.255</code> are present with Attribute 3 returning <code>Type 1</code>, the HES scheduler should manage two distinct single-shot requests.</p>
<p dir="auto">Here is the verified working sequence to program a daily 06:20:00 disconnect on <code>0.0.15.0.1.255</code>:</p>
<pre><code class="language-hex">00 01 00 01 00 01 00 1B C1 01 C1 00 16 00 00 0F 00 01 FF 04 00 01 01 02 02 09 04 06 14 00 00 09 05 FF FF FF FF FF
</code></pre>
<p dir="auto">Meter response:</p>
<pre><code class="language-hex">00 01 00 01 00 01 00 04 C5 01 C1 00
</code></pre>
<p dir="auto">And here is the verified working sequence to program the 06:25:00 reconnect on <code>0.0.15.0.3.255</code>:</p>
<pre><code class="language-hex">00 01 00 01 00 01 00 1B C1 01 C2 00 16 00 00 0F 00 03 FF 04 00 01 01 02 02 09 04 06 19 00 00 09 05 FF FF FF FF FF
</code></pre>
<p dir="auto">Meter response:</p>
<pre><code class="language-hex">00 01 00 01 00 01 00 04 C5 01 C2 00
</code></pre>
<p dir="auto">Here is the production-tested Python snippet using Gurux.DLMS to handle this fallback gracefully:</p>
<pre><code class="language-python">from gurux_dlms.objects import GXDLMSSingleActionSchedule
from gurux_dlms.objects.enums import SingleActionScheduleType
from gurux_dlms import GXDateTime
def configure_single_action_schedule(client, obis_code, exec_hour, exec_min):
schedule = GXDLMSSingleActionSchedule(obis_code)
client.read(schedule, 3) # Read Attribute 3: type
# Construct date-time structure with wildcards for daily recurring execution
target_time = GXDateTime(-1, -1, -1, exec_hour, exec_min, 0, 0)
if schedule.type == SingleActionScheduleType.SINGLE_ACTION_SCHEDULE_TYPE1:
# Firmware strictly enforces Qty = 1
schedule.executionTime = [target_time]
else:
# Extended multi-time support if enabled on advanced meters
schedule.executionTime.append(target_time)
# Generate and transmit SET-Request APDU for Attribute 4
return client.write(schedule, 4)
</code></pre>
<p dir="auto">By splitting the schedule across the dedicated <code>.1.255</code> and <code>.3.255</code> instances and respecting the <code>Type 1</code> constraint, all SET requests return <code>Data-Access-Result: success</code> (<code>0x00</code>) without risking breaker desynchronization.</p>
<p dir="auto">Supporting <a class="plugin-mentions-user plugin-mentions-a" href="/user/elena_rostova" aria-label="Profile: elena_rostova">@<bdi>elena_rostova</bdi></a>: <a class="plugin-mentions-user plugin-mentions-a" href="/user/david_miller" aria-label="Profile: david_miller">@<bdi>david_miller</bdi></a> overlooks the critical operational trap: <strong>Certificate Lifecycle and Revocation Management (CRL / OCSP) in Offline Meters</strong>!</p>
<ol>
<li>
<p dir="auto"><strong>Certificate Revocation Lists (CRL) Storage Explosion</strong>:<br />
Smart meters operate over low-bandwidth channels and cannot perform real-time online OCSP queries.<br />
An enterprise CRL containing thousands of revoked certificates is several megabytes—far exceeding an MCU's 64KB Flash limit.</p>
</li>
<li>
<p dir="auto"><strong>OTA Re-Keying Brick Risk</strong>:<br />
NIST guidelines recommend end-entity certificate lifespans of 3 to 5 years, while meters operate for 15 years.<br />
When renewing certificates over-the-air (OTA per Blue Book Chapter 4.8), any transient cellular disconnection during root cert rollover leaves the meter in an unauthenticated lockout state ('bricked'), requiring costly on-site physical replacement.</p>
</li>
</ol>
<p dir="auto">For large-scale utility operations, <strong>Paradigm A (Raw Binary A-XDR)</strong> is overwhelmingly superior for three critical reasons per Green Book Chapter 8:</p>
<ol>
<li>
<p dir="auto"><strong>Cellular Data Bandwidth Cost</strong>:<br />
A 96-point daily load profile in native compact A-XDR is <strong>~1.2 KB</strong>. The same dataset serialized to JSON with key strings expands to <strong>8.5 KB+ (7x bandwidth expansion)</strong>. Across 10 million SIM cards, this difference accounts for millions in monthly cellular carrier fees.</p>
</li>
<li>
<p dir="auto"><strong>Cryptographic Non-Repudiation</strong>:<br />
In Paradigm A, the raw frame contains the meter's GMAC authentication tag computed inside the secure element. In Paradigm B, translating to JSON at the modem breaks the cryptographic chain of custody, invalidating legal metrology audits.</p>
</li>
<li>
<p dir="auto"><strong>Firmware Decoupling</strong>:<br />
When new OBIS objects are added to meter profiles, Paradigm A requires updating only cloud parser schemas, without touching millions of field modem firmware images.</p>
</li>
</ol>
<p dir="auto">The golden rule in 104-to-DLMS gateway design per Green Book Section 7.1 is: <strong>Never synchronously poll DLMS serial meters during a 104 General Interrogation (C_IC_NA_1)</strong>.</p>
<p dir="auto">The recommended decoupled architecture:</p>
<pre><code class="language-text">[ SCADA 104 Master ] <=== TCP 2404 ===> [ Gateway Real-time Memory Cache ]
▲
Background Polling & Push Daemon
▼
[ DLMS Meter Network ] <=== RS-485/TCP 4059 <=====┘
</code></pre>
<ol>
<li><strong>Telemetry (YC / M_ME_NC_1 Float)</strong>:<br />
Gateway maintains periodic background polling loops (e.g. 1s for Voltage/Current, 15s for Energy). Master interrogation answers immediately from RAM cache.</li>
<li><strong>Tele-signaling (YX / M_SP_NA_1 Event)</strong>:<br />
DLMS Unsolicited Push (Class 40 <code>0.0.25.9.0.255</code>) alarms trigger spontaneous 104 ASDU transmissions (Cause of Transmission COT=3).</li>
<li><strong>Tele-control (YK / C_SC_NA_1 Breaker Control)</strong>:<br />
Translates directly to DLMS Class 70 (Disconnect Control <code>0.0.96.3.10.255</code>) Method 1/2.</li>
</ol>