Files
SequencerIO/arnold/__pycache__/terminator_io.cpython-311.pyc

226 lines
33 KiB
Plaintext
Raw Normal View History

2026-03-02 17:48:55 -05:00
<EFBFBD>
<00><02>i3g<00><01>&<00>dZddlmZddlZddlZddlZddlmZddlm Z ddl
m Z ddl m Z ddlmZe r
d d
lmZmZmZeje<15><00>ZeGd <0B>d <0C><00><00><00>ZGd <0A>d<0E><00>ZGd<0F>dej<00><00>ZGd<11>d<12><00>ZdS)u@
arnold/terminator_io.py — AutomationDirect Terminator I/O driver.
Encapsulates everything that touches a physical T1H-EBC100 controller:
- Modbus TCP connection management (pymodbus, auto-reconnect)
- Signal state cache (thread-safe)
- Background fast-poll thread (reads both coils and registers each cycle)
Key hardware quirks documented here:
- The EBC100 uses a UNIFIED flat coil address space across all digital
modules in physical slot order. FC02 (read discrete inputs) and
FC01/FC05/FC15 (read/write coils) share the same sequential offsets.
If slot 1 and slot 2 are 8-pt input modules (addresses 0-7, 8-15),
a 16-pt output module in slot 3 starts at coil address 16 — NOT 0.
- The EBC100 maintains TWO independent flat address spaces:
coil space (1-bit) — digital modules: FC01/FC02/FC05/FC15
register space (16-bit) — analog + temperature: FC03/FC04/FC06/FC16
A digital module advances only the coil offset; an analog module
advances only the register offset. They do not interfere.
- FC02 (read discrete inputs) returns input bits starting at address 0.
Because input modules always appear first in the unified coil scheme,
the FC02 bit index equals modbus_address for every digital input signal.
- FC04 (read input registers) returns 16-bit values for analog/temperature
input modules, starting at register address 0 in the register space.
- The EBC100 never raises Modbus exception code 2 (illegal address) for
out-of-range reads — it silently returns zeros. Module presence cannot
be auto-detected via protocol errors; use the config 'modules' list.
- The EBC100 responds to any Modbus unit/slave ID over TCP — the unit_id
field is echoed back but not used for routing. Set it to 1 (default).
- FC05 write_coil echoes back True for any address, even unmapped ones.
There is no write-error feedback for out-of-range output addresses.
- The device has no unsolicited push capability. Polling is mandatory.
Public API
----------
TerminatorIO(device: DeviceConfig)
.connect() -> bool
.disconnect()
.read_inputs() -> list[bool] | None # bulk FC02, digital inputs
.read_registers(address, count) -> list[int] | None # bulk FC04, analog inputs
.write_output(address, value) -> bool # FC05 single coil
.write_outputs(address, values) -> bool # FC15 multiple coils
.write_register(address, value) -> bool # FC06 single register
.write_registers(address, values) -> bool # FC16 multiple registers
.connected: bool
.status() -> dict
SignalState dataclass: name, value (bool|int), updated_at, stale
IORegistry(config) multi-device coordinator
.start() connect + start all poll threads
.stop() stop all poll threads + disconnect
.get(signal) -> SignalState | None
.get_value(signal) -> bool | int | None
.snapshot() -> dict[str, SignalState]
.poll_stats() -> list[dict]
.driver_status() -> list[dict]
<EFBFBD>)<01> annotationsN)<01> dataclass)<01> TYPE_CHECKING)<01>ModbusTcpClient)<01>ModbusException)<01>ExceptionResponse<73>)<03>Config<69> DeviceConfig<69> LogicalIOc<01><<00>eZdZUded<ded<ded<dZded <d
S) <0B> SignalState<74>str<74>namez
bool | int<6E>value<75>float<61>
updated_atF<EFBFBD>bool<6F>staleN)<05>__name__<5F>
__module__<EFBFBD> __qualname__<5F>__annotations__r<00><00><00>//home/noise/Code/arnold/arnold/terminator_io.pyrrXsD<00><00><00><00><00><00><00><13>O<EFBFBD>O<EFBFBD>O<EFBFBD><1A><1A><1A><1A><15><15><15><15><1C>E<EFBFBD><1C><1C><1C><1C><1C>rrc<01><><00>eZdZdZd"d<06>Zd#d<08>Zd#d <09>Zd$d
<EFBFBD>Zed#d <0B><04><00>Z d%d <0A>Z
d&d<11>Z d'd<13>Z d'd<14>Z d(d<16>Zd(d<17>Zd)d<1A>Zd*d<1B>Zd*d<1C>Zd+d<1E>Zd,d <20>Zd!S)-<2D> TerminatorIOa 
Modbus TCP driver for a single T1H-EBC100 controller.
Thread-safe: all public methods acquire an internal lock. The poll
thread holds the lock only for the duration of each FC02 call, so
write_output() will block at most one poll cycle (~50 ms).
<20>device<63>'DeviceConfig'<27>return<72>Nonec<01>|<00>||_tj<00><00>|_d|_d|_d|_d|_dS)NFr<00>)r<00> threading<6E>Lock<63>_lock<63>_client<6E>
_connected<EFBFBD>_connect_attempts<74>_last_connect_error)<02>selfrs r<00>__init__zTerminatorIO.__init__ms<<00><00><1C><04> <0B><1F>n<EFBFBD>&<26>&<26><04>
<EFBFBD>/3<><04> <0C>&+<2B><04><0F>&'<27><04><1E>&(<28><04> <20> <20> rrc<01>l<00>|j5|<00><00><00>cddd<01><00>S#1swxYwYdS)z8Open the Modbus TCP connection. Returns True on success.N)r'<00>_connect_locked<65>r,s r<00>connectzTerminatorIO.connectys}<00><00> <11>Z<EFBFBD> *<2A> *<2A><17>'<27>'<27>)<29>)<29> *<2A> *<2A> *<2A> *<2A> *<2A> *<2A> *<2A> *<2A> *<2A> *<2A> *<2A> *<2A><><EFBFBD><EFBFBD> *<2A> *<2A> *<2A> *<2A> *<2A> *<2A> <00>)<03>-<07>-c<01>6<00>|j<00>+ |j<00><00><00>n#t$rYnwxYwt|jj|jjdd<02><03><00>|_|xjdz c_|j<00><00><00>}||_ |r<t<00> d|jj |jj|jj<00><00>nLd|jj<00>d|jj<00><00>|_ t<00>d|jj |j <00><00>|S)N<>r )<04>host<73>port<72>timeout<75>retrieszConnected to %s (%s:%d)zTCP connect failed to <20>:zCould not connect to %s: %s)r(<00>close<73> Exceptionrrr5r6r*r1r)<00>log<6F>info<66>idr+<00>warning)r,<00>oks rr/zTerminatorIO._connect_locked~s;<00><00> <0F><<3C> #<23> <15><14> <0C>"<22>"<22>$<24>$<24>$<24>$<24><><1C> <15> <15> <15><14><04> <15><><EFBFBD><EFBFBD>'<27><15><1B>!<21><15><1B>!<21><15><15> 
<EFBFBD>
<EFBFBD>
<EFBFBD><04> <0C> <0A><1E><1E>!<21>#<23><1E><1E> <11>\<5C> !<21> !<21> #<23> #<23><02><1C><04><0F> <0A> B<01> <0F>H<EFBFBD>H<EFBFBD>.<2E><19>[<5B>^<5E>T<EFBFBD>[<5B>%5<>t<EFBFBD>{<7B>7G<37> I<01> I<01> I<01> I<01>O<01><14><1B>)9<>N<>N<>D<EFBFBD>K<EFBFBD><L<>N<>N<> <11> $<24> <10>K<EFBFBD>K<EFBFBD>5<><1C> <0B><0E><04>(@<40> B<01> B<01> B<01><11> s <00>#<00>
0<03>0c<01><><00>|j5|jr+ |j<00><00><00>n#t$rYnwxYwd|_d|_ddd<00><00>dS#1swxYwYdS)NF)r'r(r:r;r)r0s r<00>
disconnectzTerminatorIO.disconnect<63>s<><00><00> <11>Z<EFBFBD> #<23> #<23><13>|<7C> <19><19><18>L<EFBFBD>&<26>&<26>(<28>(<28>(<28>(<28><> <20><19><19><19><18>D<EFBFBD><19><><EFBFBD><EFBFBD>#<23>D<EFBFBD>O<EFBFBD>"<22>D<EFBFBD>L<EFBFBD> #<23> #<23> #<23> #<23> #<23> #<23> #<23> #<23> #<23> #<23> #<23> #<23><><EFBFBD><EFBFBD> #<23> #<23> #<23> #<23> #<23> #s,<00>A<03>+<02>A<03>
8<05>A<03>8<05>A<03>A<07>Ac<01><00>|jS<00>N)r)r0s r<00> connectedzTerminatorIO.connected<65>s
<00><00><13><EFBFBD>r<00>list[bool] | Nonec<01><><00>|j<00><00><00>}|dkrgS|j5|<00>d|<01><02><00>cddd<03><00>S#1swxYwYdS)a<>
Read all discrete input points in one FC02 request.
Returns a flat list of bool ordered by slot then point (matching
the unified address scheme), or None on comms error.
FC02 returns input bits starting at address 0. Because input modules
are always at lower slot numbers than output modules (enforced by the
unified address scheme), the FC02 bit index equals modbus_address for
every input signal.
r<00><02>address<73>countN)r<00>total_input_pointsr'<00> _fc02_locked)r,<00>totals r<00> read_inputszTerminatorIO.read_inputs<74>s<><00><00><15> <0B>.<2E>.<2E>0<>0<><05> <10>A<EFBFBD>:<3A>:<3A><15>I<EFBFBD> <11>Z<EFBFBD> =<3D> =<3D><17>$<24>$<24>Q<EFBFBD>e<EFBFBD>$<24><<3C><<3C> =<3D> =<3D> =<3D> =<3D> =<3D> =<3D> =<3D> =<3D> =<3D> =<3D> =<3D> =<3D><><EFBFBD><EFBFBD> =<3D> =<3D> =<3D> =<3D> =<3D> =s<00>A <03> A<07>ArI<00>intrJc<01>t<00>td<01><00>D<00>]&}|js|<00><00><00>sdS |j<00>|||jj<00><02><00>}|<04><00><00>st|t<00><00>r.t<00> d|jj |<04><00>d|_<00><>t|jd|<02><00><00>cS#tt t"f$rP}t<00> d|jj |dz|<05><00>d|_t%jd<07><00>Yd}~<05><01> d}~wwxYwdS)Nr4<00>rIrJ<00> device_idz%s FC02 error: %sFz%s read error (attempt %d): %sr <><E79A99><EFBFBD><EFBFBD><EFBFBD>?)<14>ranger)r/r(<00>read_discrete_inputsr<00>unit_id<69>isError<6F>
isinstancerr<r?r><00>list<73>bitsr<00>ConnectionError<6F>OSError<6F>time<6D>sleep<65>r,rIrJ<00>attempt<70>rr<72>excs rrLzTerminatorIO._fc02_locked<65>sL<00><00><1C>Q<EFBFBD>x<EFBFBD>x<EFBFBD> !<21> !<21>G<EFBFBD><17>?<3F> <20><1B>+<2B>+<2B>-<2D>-<2D> <20><1F>4<EFBFBD>4<EFBFBD> !<21><19>\<5C>6<>6<>#<23>5<EFBFBD>"<22>k<EFBFBD>1<>7<><12><12><02><16>:<3A>:<3A><<3C><<3C><1D>:<3A>b<EFBFBD>2C<32>#D<>#D<><1D><17>K<EFBFBD>K<EFBFBD> 3<>T<EFBFBD>[<5B>^<5E>R<EFBFBD>H<>H<>H<>&+<2B>D<EFBFBD>O<EFBFBD><1C><1B>B<EFBFBD>G<EFBFBD>F<EFBFBD>U<EFBFBD>F<EFBFBD>O<EFBFBD>,<2C>,<2C>,<2C>,<2C>,<2C><>#<23>_<EFBFBD>g<EFBFBD>><3E> !<21> !<21> !<21><13> <0B> <0B><<3C> <20>K<EFBFBD>N<EFBFBD>G<EFBFBD>a<EFBFBD>K<EFBFBD><13>><3E>><3E>><3E>"'<27><04><0F><14>
<EFBFBD>4<EFBFBD> <20> <20> <20> <20> <20> <20> <20> <20><><EFBFBD><EFBFBD><EFBFBD>  !<21><><EFBFBD><EFBFBD>
<14>t<EFBFBD><00>A=C<02>0C<02>D5<05>%AD0<05>0D5<05>list[int] | Nonec<01><><00>|dkrgS|j5|<00>||<02><00>cddd<02><00>S#1swxYwYdS)u
Read contiguous 16-bit input registers via FC04.
Used for analog and temperature input modules whose signals live
in the register address space. Returns a list of raw int values
(065535), or None on comms error.
rN)r'<00> _fc04_locked)r,rIrJs r<00>read_registerszTerminatorIO.read_registers<72>s<><00><00> <11>A<EFBFBD>:<3A>:<3A><15>I<EFBFBD> <11>Z<EFBFBD> 5<> 5<><17>$<24>$<24>W<EFBFBD>e<EFBFBD>4<>4<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<><35><EFBFBD><EFBFBD> 5<> 5<> 5<> 5<> 5<> 5s <00>3<03>7<07>7c<01>t<00>td<01><00>D<00>]&}|js|<00><00><00>sdS |j<00>|||jj<00><02><00>}|<04><00><00>st|t<00><00>r.t<00> d|jj |<04><00>d|_<00><>t|jd|<02><00><00>cS#tt t"f$rP}t<00> d|jj |dz|<05><00>d|_t%jd<07><00>Yd}~<05><01> d}~wwxYwdS)Nr4rQz%s FC04 error: %sFz#%s FC04 read error (attempt %d): %sr rS)rTr)r/r(<00>read_input_registersrrVrWrXrr<r?r>rY<00> registersrr[r\r]r^r_s rrfzTerminatorIO._fc04_locked<65>sM<00><00><1C>Q<EFBFBD>x<EFBFBD>x<EFBFBD> !<21> !<21>G<EFBFBD><17>?<3F> <20><1B>+<2B>+<2B>-<2D>-<2D> <20><1F>4<EFBFBD>4<EFBFBD> !<21><19>\<5C>6<>6<>#<23>5<EFBFBD>"<22>k<EFBFBD>1<>7<><12><12><02><16>:<3A>:<3A><<3C><<3C><1D>:<3A>b<EFBFBD>2C<32>#D<>#D<><1D><17>K<EFBFBD>K<EFBFBD> 3<>T<EFBFBD>[<5B>^<5E>R<EFBFBD>H<>H<>H<>&+<2B>D<EFBFBD>O<EFBFBD><1C><1B>B<EFBFBD>L<EFBFBD><16>%<25><16>0<>1<>1<>1<>1<>1<><31>#<23>_<EFBFBD>g<EFBFBD>><3E> !<21> !<21> !<21><13> <0B> <0B>A<> <20>K<EFBFBD>N<EFBFBD>G<EFBFBD>a<EFBFBD>K<EFBFBD><13>><3E>><3E>><3E>"'<27><04><0F><14>
<EFBFBD>4<EFBFBD> <20> <20> <20> <20> <20> <20> <20> <20><><EFBFBD><EFBFBD><EFBFBD>  !<21><><EFBFBD><EFBFBD>
<14>trcrc<01>p<00>|j5|<00>||<02><00>cddd<01><00>S#1swxYwYdS)uj
Write a single coil via FC05.
Address is the unified slot-order coil address (as stored in
LogicalIO.modbus_address). Returns True on success.
Note: the EBC100 echoes True for any address — write errors for
out-of-range addresses are silent. Config validation prevents
invalid addresses at startup.
N)r'<00> _fc05_locked<65>r,rIrs r<00> write_outputzTerminatorIO.write_output<75>s<><00><00><12>Z<EFBFBD> 5<> 5<><17>$<24>$<24>W<EFBFBD>e<EFBFBD>4<>4<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<><35><EFBFBD><EFBFBD> 5<> 5<> 5<> 5<> 5<> 5<> <00>+<03>/<07>/c<01><><00>td<01><00>D<00>]3}|js|<00><00><00>sdS |j<00>|||jj<00><03><00>}|<04><00><00>st|t<00><00>r/t<00> d|jj ||<04><00>d|_<00><>t<00> d|jj ||<02><00>dS#ttt f$rP}t<00> d|jj |dz|<05><00>d|_t#jd <09><00>Yd}~<05><01>-d}~wwxYwdS)
Nr4F<>rIrrRz%s FC05 error addr=%d: %sz%s coil[%d] = %sTz%s write error (attempt %d): %sr rS)rTr)r/r(<00>
write_coilrrVrWrXrr<r?r><00>debugrr[r\r]r^<00>r,rIrr`rarbs rrlzTerminatorIO._fc05_locked s[<00><00><1C>Q<EFBFBD>x<EFBFBD>x<EFBFBD> !<21> !<21>G<EFBFBD><17>?<3F> !<21><1B>+<2B>+<2B>-<2D>-<2D>!<21> <20>5<EFBFBD>5<EFBFBD> !<21><19>\<5C>,<2C>,<2C>#<23>5<EFBFBD>"<22>k<EFBFBD>1<>-<2D><12><12><02><16>:<3A>:<3A><<3C><<3C><1D>:<3A>b<EFBFBD>2C<32>#D<>#D<><1D><17>K<EFBFBD>K<EFBFBD> ;<3B> $<24> <0B><0E><07><12>=<3D>=<3D>=<3D>&+<2B>D<EFBFBD>O<EFBFBD><1C><13> <09> <09>,<2C>d<EFBFBD>k<EFBFBD>n<EFBFBD>g<EFBFBD>u<EFBFBD>M<>M<>M<><1B>t<EFBFBD>t<EFBFBD><74>#<23>_<EFBFBD>g<EFBFBD>><3E> !<21> !<21> !<21><13> <0B> <0B>=<3D> <20>K<EFBFBD>N<EFBFBD>G<EFBFBD>a<EFBFBD>K<EFBFBD><13>><3E>><3E>><3E>"'<27><04><0F><14>
<EFBFBD>4<EFBFBD> <20> <20> <20> <20> <20> <20> <20> <20><><EFBFBD><EFBFBD><EFBFBD>  !<21><><EFBFBD><EFBFBD>
<15>u<EFBFBD><00>A>C<02>1'C<02>E<05>2AD=<05>=E<05>values<65>
list[bool]c <01><><00>|j5td<01><00>D<00>]"}|js"|<00><00><00>sddd<02><00>dS |j<00>|||jj<00><04><00>}|<04><00><00>st|t<00><00>r/t<00> d|jj ||<04><00>d|_<00><>ddd<02><00>dS#ttt f$rP}t<00> d|jj |dz|<05><00>d|_t#jd <09><00>Yd}~<05><01>d}~wwxYw ddd<02><00>n #1swxYwYdS)
z)Write multiple contiguous coils via FC15.r4NF<4E>rIrvrRz%s FC15 error addr=%d: %sTz%%s write_coils error (attempt %d): %sr rS)r'rTr)r/r(<00> write_coilsrrVrWrXrr<r?r>rr[r\r]r^<00>r,rIrvr`rarbs r<00> write_outputszTerminatorIO.write_outputs$s<><00><00> <11>Z<EFBFBD> %<25> %<25> <20><11>8<EFBFBD>8<EFBFBD> %<25> %<25><07><1B><EFBFBD>%<25><1F>/<2F>/<2F>1<>1<>%<25>$<24>  %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25>
%<25><1D><1C>1<>1<> '<27><06>"&<26>+<2B>"5<>2<><16><16>B<EFBFBD><1A>z<EFBFBD>z<EFBFBD>|<7C>|<7C>!<21>z<EFBFBD>"<22>6G<36>'H<>'H<>!<21><1B> <0B> <0B>$?<3F>$(<28>K<EFBFBD>N<EFBFBD>G<EFBFBD>R<EFBFBD>A<01>A<01>A<01>*/<2F><04><0F> <20><1F> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25><> (<28><1F>'<27>B<>%<25>%<25>%<25><17>K<EFBFBD>K<EFBFBD> G<> $<24> <0B><0E><07>!<21> <0B>S<EFBFBD>B<01>B<01>B<01>&+<2B>D<EFBFBD>O<EFBFBD><18>J<EFBFBD>t<EFBFBD>$<24>$<24>$<24>$<24>$<24>$<24>$<24>$<24><><EFBFBD><EFBFBD><EFBFBD> %<25><><EFBFBD><EFBFBD> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25><><EFBFBD><EFBFBD> %<25> %<25> %<25> %<25>*<15>u<EFBFBD>=<00>/E <03>A>C<04>E <03>D9<07>)AD4<07>.E <03>4D9<07>9E <03> E <07>E c<01>p<00>|j5|<00>||<02><00>cddd<01><00>S#1swxYwYdS)u<>
Write a single 16-bit holding register via FC06.
Address is the register-space address (as stored in
LogicalIO.modbus_address for analog output signals).
value is a raw 16-bit integer (065535).
N)r'<00> _fc06_lockedrms r<00>write_registerzTerminatorIO.write_registerAs<><00><00><12>Z<EFBFBD> 5<> 5<><17>$<24>$<24>W<EFBFBD>e<EFBFBD>4<>4<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<> 5<><35><EFBFBD><EFBFBD> 5<> 5<> 5<> 5<> 5<> 5roc<01><><00>td<01><00>D<00>]3}|js|<00><00><00>sdS |j<00>|||jj<00><03><00>}|<04><00><00>st|t<00><00>r/t<00> d|jj ||<04><00>d|_<00><>t<00> d|jj ||<02><00>dS#ttt f$rP}t<00> d|jj |dz|<05><00>d|_t#jd <09><00>Yd}~<05><01>-d}~wwxYwdS)
Nr4Frqz%s FC06 error addr=%d: %sz%s reg[%d] = %dTz$%s FC06 write error (attempt %d): %sr rS)rTr)r/r(r<>rrVrWrXrr<r?r>rsrr[r\r]r^rts rrzTerminatorIO._fc06_lockedLs[<00><00><1C>Q<EFBFBD>x<EFBFBD>x<EFBFBD> !<21> !<21>G<EFBFBD><17>?<3F> !<21><1B>+<2B>+<2B>-<2D>-<2D>!<21> <20>5<EFBFBD>5<EFBFBD> !<21><19>\<5C>0<>0<>#<23>5<EFBFBD>"<22>k<EFBFBD>1<>1<><12><12><02><16>:<3A>:<3A><<3C><<3C><1D>:<3A>b<EFBFBD>2C<32>#D<>#D<><1D><17>K<EFBFBD>K<EFBFBD> ;<3B> $<24> <0B><0E><07><12>=<3D>=<3D>=<3D>&+<2B>D<EFBFBD>O<EFBFBD><1C><13> <09> <09>+<2B>T<EFBFBD>[<5B>^<5E>W<EFBFBD>e<EFBFBD>L<>L<>L<><1B>t<EFBFBD>t<EFBFBD><74>#<23>_<EFBFBD>g<EFBFBD>><3E> !<21> !<21> !<21><13> <0B> <0B>B<> <20>K<EFBFBD>N<EFBFBD>G<EFBFBD>a<EFBFBD>K<EFBFBD><13>><3E>><3E>><3E>"'<27><04><0F><14>
<EFBFBD>4<EFBFBD> <20> <20> <20> <20> <20> <20> <20> <20><><EFBFBD><EFBFBD><EFBFBD>  !<21><><EFBFBD><EFBFBD>
<15>uru<00> list[int]c <01><><00>|j5td<01><00>D<00>]"}|js"|<00><00><00>sddd<02><00>dS |j<00>|||jj<00><04><00>}|<04><00><00>st|t<00><00>r/t<00> d|jj ||<04><00>d|_<00><>ddd<02><00>dS#ttt f$rP}t<00> d|jj |dz|<05><00>d|_t#jd <09><00>Yd}~<05><01>d}~wwxYw ddd<02><00>n #1swxYwYdS)
z<Write multiple contiguous 16-bit holding registers via FC16.r4NFryz%s FC16 error addr=%d: %sTz$%s FC16 write error (attempt %d): %sr rS)r'rTr)r/r(<00>write_registersrrVrWrXrr<r?r>rr[r\r]r^r{s rr<>zTerminatorIO.write_registersds<><00><00> <11>Z<EFBFBD> %<25> %<25> <20><11>8<EFBFBD>8<EFBFBD> %<25> %<25><07><1B><EFBFBD>%<25><1F>/<2F>/<2F>1<>1<>%<25>$<24>  %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25>
%<25><1D><1C>5<>5<> '<27><06>"&<26>+<2B>"5<>6<><16><16>B<EFBFBD><1A>z<EFBFBD>z<EFBFBD>|<7C>|<7C>!<21>z<EFBFBD>"<22>6G<36>'H<>'H<>!<21><1B> <0B> <0B>$?<3F>$(<28>K<EFBFBD>N<EFBFBD>G<EFBFBD>R<EFBFBD>A<01>A<01>A<01>*/<2F><04><0F> <20><1F> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25><> (<28><1F>'<27>B<>%<25>%<25>%<25><17>K<EFBFBD>K<EFBFBD> F<> $<24> <0B><0E><07>!<21> <0B>S<EFBFBD>B<01>B<01>B<01>&+<2B>D<EFBFBD>O<EFBFBD><18>J<EFBFBD>t<EFBFBD>$<24>$<24>$<24>$<24>$<24>$<24>$<24>$<24><><EFBFBD><EFBFBD><EFBFBD> %<25><><EFBFBD><EFBFBD> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25><><EFBFBD><EFBFBD> %<25> %<25> %<25> %<25>*<15>ur}<00>dictc<01>r<00>|jj|jj|jj|j|j|jpdd<01>S)N)rRr5r6rE<00>connect_attempts<74>
last_error)rr>r5r6r)r*r+r0s r<00>statuszTerminatorIO.status<75>s><00><00> $<24> <0B><0E> $<24> <0B> 0<> $<24> <0B> 0<> $<24><0F> $<24> 6<> $<24> 8<> @<40>D<EFBFBD> 
<EFBFBD>
<EFBFBD>
rN)rr r!r")r!r<00>r!r")r!rF)rIrOrJrOr!rF)rIrOrJrOr!rd)rIrOrrr!r)rIrOrvrwr!r)rIrOrrOr!r)rIrOrvr<>r!r<00>r!r<>)rrr<00>__doc__r-r1r/rB<00>propertyrErNrLrgrfrnrlr|r<>rr<>r<>rrrrrdsj<00><00><00><00><00><00><08><08>)<29>)<29>)<29>)<29>*<2A>*<2A>*<2A>*<2A>
<12><12><12><12>6#<23>#<23>#<23>#<23><0E><1F><1F><1F><0E>X<EFBFBD><1F>=<3D>=<3D>=<3D>=<3D>$<14><14><14><14>4 5<> 5<> 5<> 5<><14><14><14><14>4 5<> 5<> 5<> 5<><15><15><15><15>0<15><15><15><15>: 5<> 5<> 5<> 5<><15><15><15><15>0<15><15><15><15>:
<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>
rrc<01>\<00><00>eZdZdZd<15>fd <0A> Zedd<0F><04><00>Zdd<10>Zdd<11>Zdd<12>Z dd<14>Z
<EFBFBD>xZ S)<19> _PollThreadu]
Reads all input points from one EBC100 at poll_interval_ms, updates the
shared signal cache. Daemon thread — exits when the process does.
Each poll cycle reads BOTH address spaces:
- FC02 (coil space): digital input signals → list[bool]
- FC04 (register space): analog/temperature input signals → list[int]
<20>driverr<00>digital_signals<6C>list['LogicalIO']<5D>analog_signals<6C>cache<68>dict[str, SignalState]<5D>lock<63>threading.Lockr!r"c<01><00><01>t<00><00><00>d|jj<00><00>d<02><03><00>||_||_||_||_||_tj
<00><00>|_ d|_ d|_ d|_d|_dS)Nzpoll-T)r<00>daemonrg)<10>superr-rr><00>_driver<65>_digital_signals<6C>_analog_signals<6C>_cacher'r%<00>Event<6E>_stop<6F>
poll_count<EFBFBD> error_count<6E> _achieved_hz<68> _last_poll_ts)r,r<>r<>r<>r<>r<><00> __class__s <20>rr-z_PollThread.__init__<5F>s<><00><><00> <0E><07><07><18><18>8<>f<EFBFBD>m<EFBFBD>&6<>8<>8<><14><18>F<>F<>F<> &<26><04> <0C> /<2F><04><1D> .<2E><04><1C> %<25><04> <0B> $<24><04>
<EFBFBD>"<22><1F>*<2A>*<2A><04>
<EFBFBD><1C><04><0F><1C><04><18>#&<26><04><19>+/<2F><04><1A><1A>rrOc<01>T<00>t|j<00><00>t|j<00><00>zSrD)<03>lenr<6E>r<>r0s r<00>_total_signalsz_PollThread._total_signals<6C>s#<00><00><12>4<EFBFBD>(<28>)<29>)<29>C<EFBFBD><04>0D<30>,E<>,E<>E<>Erc<01>8<00>|j<00><00><00>dSrD)r<><00>setr0s r<00>stopz_PollThread.stop<6F>s<00><00> <0C>
<EFBFBD><0E><0E><18><18><18><18>rc <01><00>|jjjdz }t<00>d|jjj|jjjt |j<00><00>t |j<00><00><00><00>|j<00> <00><00>tj <00><00>}d}|j <00> <00><00><00>stj <00><00>}|<00><00><00>|dz }|xjdz c_tj <00><00>|z
}tj <00><00>|z
}|dkrU||z |_t<00>d|jjj|j|j<00><00>tj <00><00>}d}||z
}|dkr|j <00>|<07><00>|j <00> <00><00><00><01>t<00>d|jjj<00><00>|j<00><00><00>dS)Ng@<40>@zIPoll thread started: %s %.0f ms interval %d digital + %d analog signalsrr g@z%s %.1f polls/s errors=%dzPoll thread stopped: %s)r<>r<00>poll_interval_msr<r=r>r<>r<>r<>r1r]<00> monotonicr<63><00>is_set<65>_cycler<65>r<>rsr<><00>waitrB)r,<00>interval<61>rate_t0<74>
rate_polls<EFBFBD>t0<74>elapsed<65>windowr<77>s r<00>runz_PollThread.run<75>s<><00><00><17><<3C>&<26>7<>&<26>@<40><08> <0B><08><08>\<5C><15><1C>$<24>'<27><15><1C>$<24>5<><14>T<EFBFBD>*<2A>+<2B>+<2B><14>T<EFBFBD>)<29>*<2A>*<2A>  ,<2C> ,<2C> ,<2C> <0A> <0C><1C><1C><1E><1E><1E><19>^<5E>%<25>%<25><07><16>
<EFBFBD><16>*<2A>#<23>#<23>%<25>%<25> &<26><15><1E>!<21>!<21>B<EFBFBD> <10>K<EFBFBD>K<EFBFBD>M<EFBFBD>M<EFBFBD>M<EFBFBD> <16>Q<EFBFBD> <1E>J<EFBFBD> <10>O<EFBFBD>O<EFBFBD>q<EFBFBD> <20>O<EFBFBD>O<EFBFBD><1A>n<EFBFBD>&<26>&<26><12>+<2B>G<EFBFBD><1A>^<5E>%<25>%<25><07>/<2F>F<EFBFBD><15><13>}<7D>}<7D>$.<2E><16>$7<><04>!<21><13> <09> <09>7<><1E>,<2C>-<2D>0<><1E>+<2B>T<EFBFBD>-=<3D>?<3F>?<3F>?<3F>"<22>^<5E>-<2D>-<2D><07><1E>
<EFBFBD><1B>g<EFBFBD>%<25>D<EFBFBD><13>a<EFBFBD>x<EFBFBD>x<EFBFBD><14>
<EFBFBD><0F><0F><04>%<25>%<25>%<25>)<17>*<2A>#<23>#<23>%<25>%<25> &<26>, <0C><08><08>*<2A>D<EFBFBD>L<EFBFBD>,?<3F>,B<>C<>C<>C<> <0C> <0C><1F><1F>!<21>!<21>!<21>!<21>!rc
<01><><00>|js |jsdSd}i}tj<00><00>}|j<00>r|j<00><00><00>}|<04>^d}|jD]S}|j<00>|j<00><00>}t|j|r|j
nd|r|j n|d<02><03><00>||j<<00>Tn<54>|jD]<5D>}|j t|<04><00>kr9t|jt||j <00><00>|d<01><03><00>||j<<00>St<00>d|jjj|j|j t|<04><00><00><00><00><>|j<00>r9|jj<00><00><00>}|j<00>d|<07><06><00>}|<08>^d}|jD]S}|j<00>|j<00><00>}t|j|r|j
nd|r|j n|d<02><03><00>||j<<00>Tn<54>|jD]<5D>}|j t|<08><00>kr9t|jt+||j <00><00>|d<01><03><00>||j<<00>St<00>d|jjj|j|j t|<08><00><00><00><00><>|r|xjdz c_||_|j5|j<00>|<02><00>ddd<00><00>dS#1swxYwYdS) NFT)rrrrz+%s signal %r addr %d out of range (%d bits)rrHz/%s signal %r reg addr %d out of range (%d regs)r )r<>r<>r]r<>r<>rNr<><00>getrrrr<00>modbus_addressr<73>rr<r?rr><00>total_analog_input_channelsrgrOr<>r<>r'<00>update) r,<00> had_error<6F>updates<65>nowrZ<00>sig<69>existing<6E>
total_regs<EFBFBD>regss rr<>z_PollThread._cycle<6C>s|<00><00><13>$<24> <13>T<EFBFBD>-A<> <13> <12>F<EFBFBD><19> <09>*,<2C><07><12>n<EFBFBD><1E><1E><03> <10> <20> C<01><17><<3C>+<2B>+<2B>-<2D>-<2D>D<EFBFBD><13>|<7C> <20> <09><1F>0<><16><16>C<EFBFBD>#<23>{<7B><EFBFBD><EFBFBD>s<EFBFBD>x<EFBFBD>8<>8<>H<EFBFBD>(3<> <20>X<EFBFBD>08<30>C<>h<EFBFBD>n<EFBFBD>n<EFBFBD>e<EFBFBD>:B<>#K<>8<EFBFBD>#6<>#6<><03>"<22> )<16>)<16>)<16>G<EFBFBD>C<EFBFBD>H<EFBFBD>%<25>%<25><16> <20>0<> C<01> C<01>C<EFBFBD><1A>)<29>C<EFBFBD><04>I<EFBFBD>I<EFBFBD>5<>5<>,7<>!$<24><18>"&<26>t<EFBFBD>C<EFBFBD>,><3E>'?<3F>"@<40>"@<40>'*<2A>"'<27> -<1A>-<1A>-<1A><07><03><08>)<29>)<29><1C> <0B> <0B>$Q<>$(<28>L<EFBFBD>$7<>$:<3A>C<EFBFBD>H<EFBFBD>$'<27>$6<><03>D<EFBFBD> <09> <09>C<01>C<01>C<01>C<01>
<10> <1F> C<01><1D><1C>,<2C>H<>H<>J<>J<>J<EFBFBD><17><<3C>.<2E>.<2E>q<EFBFBD>
<EFBFBD>.<2E>K<>K<>D<EFBFBD><13>|<7C> <20> <09><1F>/<2F><16><16>C<EFBFBD>#<23>{<7B><EFBFBD><EFBFBD>s<EFBFBD>x<EFBFBD>8<>8<>H<EFBFBD>(3<> <20>X<EFBFBD>08<30>?<3F>h<EFBFBD>n<EFBFBD>n<EFBFBD>a<EFBFBD>:B<>#K<>8<EFBFBD>#6<>#6<><03>"<22> )<16>)<16>)<16>G<EFBFBD>C<EFBFBD>H<EFBFBD>%<25>%<25><16> <20>/<2F> C<01> C<01>C<EFBFBD><1A>)<29>C<EFBFBD><04>I<EFBFBD>I<EFBFBD>5<>5<>,7<>!$<24><18>"%<25>d<EFBFBD>3<EFBFBD>+=<3D>&><3E>"?<3F>"?<3F>'*<2A>"'<27> -<1A>-<1A>-<1A><07><03><08>)<29>)<29><1C> <0B> <0B>$U<>$(<28>L<EFBFBD>$7<>$:<3A>C<EFBFBD>H<EFBFBD>$'<27>$6<><03>D<EFBFBD> <09> <09>C<01>C<01>C<01>C<01> <15> "<22> <10> <1C> <1C><01> !<21> <1C> <1C> <20><04><1A> <11>Z<EFBFBD> (<28> (<28> <10>K<EFBFBD> <1E> <1E>w<EFBFBD> '<27> '<27> '<27> (<28> (<28> (<28> (<28> (<28> (<28> (<28> (<28> (<28> (<28> (<28> (<28><><EFBFBD><EFBFBD> (<28> (<28> (<28> (<28> (<28> (s<00>)K<03>K<07>Kr<>c<01><><00>|jjj|j|jt |jd<01><00>t d|jjjz d<01><00>|j|<00> <00><00>d<03>S)Nr i<>)rRr<>r<><00> achieved_hz<68> target_hz<68> last_poll_ts<74>running)
r<EFBFBD>rr>r<>r<><00>roundr<64>r<>r<><00>is_aliver0s r<00>statsz_PollThread.stats#sb<00><00> <20>L<EFBFBD>/<2F>2<> <20>O<EFBFBD> <20>,<2C>!<21>$<24>"3<>Q<EFBFBD>7<>7<>!<21>$<24><14><1C>)<<3C>)M<>"M<>q<EFBFBD>Q<>Q<> <20>.<2E> <20>M<EFBFBD>M<EFBFBD>O<EFBFBD>O<EFBFBD>
<EFBFBD>
<EFBFBD>
r) r<>rr<>r<>r<>r<>r<>r<>r<>r<>r!r")r!rOr<>r<>) rrrr<>r-r<>r<>r<>r<>r<>r<><00> __classcell__)r<>s@rr<>r<><00>s<><00><><00><00><00><00><00><08><08>0<>0<>0<>0<>0<>0<>*<0E>F<01>F<01>F<01><0E>X<EFBFBD>F<01><19><19><19><19>$"<22>$"<22>$"<22>$"<22>LE(<28>E(<28>E(<28>E(<28>N 
<EFBFBD> 
<EFBFBD> 
<EFBFBD> 
<EFBFBD> 
<EFBFBD> 
<EFBFBD> 
<EFBFBD> 
rr<>c<01>b<00>eZdZdZdd<06>Zdd<07>Zdd<08>Zdd <0C>Zdd<0E>Zdd<10>Z dd<12>Z
d d<15>Z d!d<17>Z d!d<18>Z dS)"<22>
IORegistrya
Owns all TerminatorIO drivers and poll threads for the full config.
Usage:
registry = IORegistry(config)
registry.start() # connect + begin polling
...
val = registry.get_value("my_signal")
registry.stop()
<20>config<69>'Config'r!r"c<01><><00><06>||_i|_tj<00><00>|_i|_g|_|jD]<5D><>t<00><06><00>}||j<00>j <t<00>fd<01>|j D<00><00>d<02><00><03><00>}t<00>fd<04>|j D<00><00>d<05><00><03><00>}t||||j|j<00><00>}|j<00> |<05><00><00><>dS)Nc3<01>h<00>K<00>|],}|j<00>jkr|jdkr|jdk<00>(|V<00><00>-dS)<03>input<75>coilN<6C>rr><00> direction<6F> modbus_space<63><03>.0<EFBFBD>srs <20>r<00> <genexpr>z&IORegistry.__init__.<locals>.<genexpr>NsY<00><><00><00><00>/<2F>/<2F>q<EFBFBD><15>H<EFBFBD><06> <09>)<29>)<29><16>[<5B>G<EFBFBD>+<2B>+<2B><16>^<5E>v<EFBFBD>-<2D>-<2D><13>.<2E>-<2D>-<2D>-<2D>/<2F>/rc<01><00>|jSrD<00>r<><00>r<>s r<00><lambda>z%IORegistry.__init__.<locals>.<lambda>R<00> <00><00>a<EFBFBD>.<2E>r)<01>keyc3<01>h<00>K<00>|],}|j<00>jkr|jdkr|jdk<00>(|V<00><00>-dS)r<><00>registerNr<4E>r<>s <20>rr<>z&IORegistry.__init__.<locals>.<genexpr>UsY<00><><00><00><00>3<>3<>q<EFBFBD><15>H<EFBFBD><06> <09>)<29>)<29><16>[<5B>G<EFBFBD>+<2B>+<2B><16>^<5E>z<EFBFBD>1<>1<><13>2<>1<>1<>1<>3<>3rc<01><00>|jSrDr<>r<>s rr<>z%IORegistry.__init__.<locals>.<lambda>Yr<>r)<0E>_configr<67>r%r&r'<00>_drivers<72>_pollers<72>devicesrr><00>sorted<65>
logical_ior<EFBFBD><00>append)r,r<>r<><00>digital_inputs<74> analog_inputs<74>pollerrs @rr-zIORegistry.__init__?s.<00><><00><1E><04> <0C>02<30><04> <0B>"<22><1E>)<29>)<29><04>
<EFBFBD>24<32><04> <0A>13<31><04> <0A><1C>n<EFBFBD> )<29> )<29>F<EFBFBD>!<21>&<26>)<29>)<29>F<EFBFBD>'-<2D>D<EFBFBD>M<EFBFBD>&<26>)<29> $<24>$<24>/<2F>/<2F>/<2F>/<2F>F<EFBFBD>-<2D>/<2F>/<2F>/<2F>/<2F>.<2E> <0E><0E><0E>N<EFBFBD>#<23>3<>3<>3<>3<>F<EFBFBD>-<2D>3<>3<>3<>/<2F>.<2E> <0E><0E><0E>M<EFBFBD>!<21><16><0E> <0A><14> <0B>T<EFBFBD>Z<EFBFBD><0E><0E>F<EFBFBD> <11>M<EFBFBD> <20> <20><16> (<28> (<28> (<28> (<28>/ )<29> )rc<01>B<00>|jD]}|<01><00><00><00>dS)zEStart all poll threads (each connects its own driver on first cycle).N)r<><00>start<72>r,<00>ps rr<>zIORegistry.startes,<00><00><15><1D> <16> <16>A<EFBFBD> <0A>G<EFBFBD>G<EFBFBD>I<EFBFBD>I<EFBFBD>I<EFBFBD>I<EFBFBD> <16> rc<01><><00>|jD]}|<01><00><00><00>|jD]}|<01>d<01><02><00><00>dS)z1Stop all poll threads and disconnect all drivers.<2E>)r7N)r<>r<><00>joinr<6E>s rr<>zIORegistry.stopjsT<00><00><15><1D> <15> <15>A<EFBFBD> <0A>F<EFBFBD>F<EFBFBD>H<EFBFBD>H<EFBFBD>H<EFBFBD>H<EFBFBD><15><1D> <1E> <1E>A<EFBFBD> <0A>F<EFBFBD>F<EFBFBD>1<EFBFBD>F<EFBFBD> <1D> <1D> <1D> <1D> <1E> r<00> signal_namer<00>SignalState | Nonec<01>x<00>|j5|j<00>|<01><00>cddd<00><00>S#1swxYwYdSrD)r'r<>r<>)r,r<>s rr<>zIORegistry.getus<00><00> <11>Z<EFBFBD> 0<> 0<><17>;<3B>?<3F>?<3F>;<3B>/<2F>/<2F> 0<> 0<> 0<> 0<> 0<> 0<> 0<> 0<> 0<> 0<> 0<> 0<><30><EFBFBD><EFBFBD> 0<> 0<> 0<> 0<> 0<> 0s <00>/<03>3<07>3<07>bool | int | Nonec<01><><00>|j5|j<00>|<01><00>}|<02>|jndcddd<00><00>S#1swxYwYdSrD)r'r<>r<>r<00>r,r<>r<>s r<00> get_valuezIORegistry.get_valuey<00><><00><00> <11>Z<EFBFBD> 6<> 6<><14> <0B><0F><0F> <0B>,<2C>,<2C>A<EFBFBD><1F>m<EFBFBD>1<EFBFBD>7<EFBFBD>7<EFBFBD><14> 6<> 6<> 6<> 6<> 6<> 6<> 6<> 6<> 6<> 6<> 6<> 6<><36><EFBFBD><EFBFBD> 6<> 6<> 6<> 6<> 6<> 6<> <00>%:<03>><07>>rc<01><><00>|j5|j<00>|<01><00>}|<02>|jndcddd<00><00>S#1swxYwYdS)NT)r'r<>r<>rr<>s r<00>is_stalezIORegistry.is_stale~r<>r<>r<>c<01>l<00>|j5t|j<00><00>cddd<01><00>S#1swxYwYdS)z&Shallow copy of the full signal cache.N)r'r<>r<>r0s r<00>snapshotzIORegistry.snapshot<6F>s{<00><00> <11>Z<EFBFBD> %<25> %<25><17><04> <0B>$<24>$<24> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25> %<25><><EFBFBD><EFBFBD> %<25> %<25> %<25> %<25> %<25> %r2rR<00>TerminatorIO | Nonec<01>6<00>|j<00>|<01><00>SrD)r<>r<>)r,rRs rr<>zIORegistry.driver<65>s<00><00><13>}<7D> <20> <20><19>+<2B>+<2B>+r<00>
list[dict]c<01>H<00>d<01>|j<00><00><00>D<00><00>S)Nc<01>6<00>g|]}|<01><00><00><00><02>Sr)r<>)r<><00>ds r<00>
<listcomp>z,IORegistry.driver_status.<locals>.<listcomp><3E>s <00><00>;<3B>;<3B>;<3B>q<EFBFBD><01><08><08>
<EFBFBD>
<EFBFBD>;<3B>;<3B>;r)r<>rvr0s r<00> driver_statuszIORegistry.driver_status<75>s$<00><00>;<3B>;<3B>D<EFBFBD>M<EFBFBD>$8<>$8<>$:<3A>$:<3A>;<3B>;<3B>;<3B>;rc<01>$<00>d<01>|jD<00><00>S)Nc<01>6<00>g|]}|<01><00><00><00><02>Sr)r<>)r<>r<>s rr
z)IORegistry.poll_stats.<locals>.<listcomp><3E>s <00><00>1<>1<>1<>a<EFBFBD><01><07><07> <09> <09>1<>1<>1r)r<>r0s r<00>
poll_statszIORegistry.poll_stats<74>s<00><00>1<>1<>4<EFBFBD>=<3D>1<>1<>1<>1rN)r<>r<>r!r"r<>)r<>rr!r<>)r<>rr!r<>)r<>rr!r)r!r<>)rRrr!r)r!r)rrrr<>r-r<>r<>r<>r<>rrr<>r rrrrr<>r<>3s<><00><00><00><00><00><00> <08> <08> )<29> )<29> )<29> )<29>L<16><16><16><16>
<1E><1E><1E><1E>0<>0<>0<>0<>6<>6<>6<>6<>
6<>6<>6<>6<>
%<25>%<25>%<25>%<25>,<2C>,<2C>,<2C>,<2C><<3C><<3C><<3C><<3C>2<>2<>2<>2<>2<>2rr<>)r<><00>
__future__r<00>loggingr%r]<00> dataclassesr<00>typingr<00>pymodbus.clientr<00>pymodbus.exceptionsr<00> pymodbus.pdurr<>r
r r <00> getLoggerrr<rr<00>Threadr<64>r<>rrr<00><module>rs<><00><01>?<04>?<04>B#<23>"<22>"<22>"<22>"<22>"<22><0E><0E><0E><0E><10><10><10><10> <0B> <0B> <0B> <0B>!<21>!<21>!<21>!<21>!<21>!<21> <20> <20> <20> <20> <20> <20>+<2B>+<2B>+<2B>+<2B>+<2B>+<2B>/<2F>/<2F>/<2F>/<2F>/<2F>/<2F>*<2A>*<2A>*<2A>*<2A>*<2A>*<2A><10>8<>7<>7<>7<>7<>7<>7<>7<>7<>7<>7<><17>g<EFBFBD><17><08>!<21>!<21><03> <0B><1D><1D><1D><1D><1D><1D><1D> <0B><19><1D>e
<EFBFBD>e
<EFBFBD>e
<EFBFBD>e
<EFBFBD>e
<EFBFBD>e
<EFBFBD>e
<EFBFBD>e
<EFBFBD>X \
<EFBFBD>\
<EFBFBD>\
<EFBFBD>\
<EFBFBD>\
<EFBFBD>)<29>"<22>\
<EFBFBD>\
<EFBFBD>\
<EFBFBD>Fd2<>d2<>d2<>d2<>d2<>d2<>d2<>d2<>d2<>d2r