o
    1
 jþ8  ã                   @   sZ   d Z ddlZddlZddlZe e¡ZdZdZdd„ Z	dd„ Z
d	d
„ Zdd„ Zdd„ ZdS )a�  
Miner API Layer - Whatsminer M60 data collection.
Standalone module with no GUI dependency.

Antminer (stock) and Braiins collectors plus the firmware-detection probe were
removed 2026-06-02: the fleet is 100% Whatsminer M60, so callers go straight to
collect_whatsminer() instead of paying ~4s of failing Braiins/Antminer probes
per scan. See git history for the retired collectors.
é    Né   i¼  c                 C   s
  t   t jt j¡}| t¡ zr| | tf¡ | t 	d|i¡ 
¡ d ¡ d}t|ƒdk rC| d¡}|s3n||7 }| d¡r=nt|ƒdk s+|jddd� d	d
¡}|sWW | ¡  dS | d¡}| d¡}|dkry||kryt |||d … ¡W | ¡  S W | ¡  dS | ¡  w )zHSend a CGMiner-style command over TCP port 4028 (Whatsminer devdetails).Úcommandó    ó    i   i    úutf-8Úignore©Úerrorsú Ú Nú{ú}r   é   )ÚsocketÚAF_INETÚSOCK_STREAMÚ
settimeoutÚTIMEOUT_TCPÚconnectÚCGMINER_PORTÚsendallÚjsonÚdumpsÚencodeÚlenÚrecvÚendswithÚdecodeÚreplaceÚcloseÚfindÚrfindÚloads)Úipr   ÚsockÚbufÚchunkÚtextÚstartÚend© r*   ú,/var/www/html/ngon/plucker/app/miner_apis.pyÚcgminer_command   s2   


ú
ú

þr,   c                 C   sÞ   t   t jt j¡}| t¡ z\| | tf¡ | t 	d|i¡ 
¡ ¡ d}t|ƒdk rM| d¡}|s1n||7 }| d¡| d¡krG| d¡dkrGnt|ƒdk s)|jdd	d
� d¡}|s`W | ¡  dS t |¡W | ¡  S | ¡  w )zLSend a Whatsminer-style command over TCP port 4028 (read-only, unencrypted).Úcmdr   i   i   ó   {ó   }r   r   r   r   r
   N)r   r   r   r   r   r   r   r   r   r   r   r   r   Úcountr   Ústripr   r"   )r#   r-   r$   r%   r&   r'   r*   r*   r+   Úwhatsminer_command5   s(   

"ú
þr2   c              	   C   sF   zt | ƒ}W n ttfy   Y dS w |dkrdS |dk r!|d S |S )zËNormalize a Whatsminer hashrate reading to MH/s.

    2024 firmware reports MH/s (~57,000,000/board); 2025+ firmware reports
    TH/s (~57/board). Values below 1e6 are treated as TH/s and scaled up.
    Nr   g        i@B )ÚfloatÚ	TypeErrorÚ
ValueError)ÚvalueÚvr*   r*   r+   Ú_to_mhsP   s   ÿr8   c              	   C   s¼  |   d¡pg }|D ]*}dD ]}|  |¡durt|| ƒ||< q|  d¡du r3|  d¡dur3|d |d< q	|   d¡du rL|rLtdd„ |D ƒƒ}|rL|| d< |   d¡dur\t| d ƒ| d< n|rmtd	d„ |D ƒƒ}|rm|| d< |   d
¡du r“|   d¡dur“ztt| d ƒƒ| d
< W n ttfy’   Y nw |   d¡du r¹|   d¡r¹tdd„ | d D ƒƒ| d< tdd„ | d D ƒƒ| d< |   d¡du rÑ|rÑdd„ |D ƒ}|rÑt|ƒ| d< |D ]}| dd¡ qÓdS )a°  Fill missing mining fields and normalize hashrate units across firmwares.

    2025+ Whatsminer firmware strips the cgminer `summary` response (no
    "MHS 5s"/"Power"/"Power Limit") and reports hashrate in TH/s instead of
    MH/s. `devs`/`get_psu`/`status` still work on every firmware, so the data
    is recovered from there. All hashrate fields are normalized to MH/s, which
    is the unit the frontend MinerView expects.
    Ú
hashboards)Úhashrate_mhs_5sÚhashrate_mhs_avgÚhashrate_mhs_rtNr:   r<   c                 s   ó"   � | ]}|  d ¡r|d  V  qdS )r:   N©Úget©Ú.0Úbr*   r*   r+   Ú	<genexpr>u   ó   €  z1_reconcile_whatsminer_firmware.<locals>.<genexpr>r;   c                 s   r=   )r;   Nr>   r@   r*   r*   r+   rC   }   rD   Úpower_wattsÚpsu_power_inÚacceptedÚpoolsc                 s   ó   � | ]
}|  d ¡p
dV  qdS )rG   r   Nr>   ©rA   Úpr*   r*   r+   rC   Š   ó   € c                 s   rI   )Úrejectedr   Nr>   rJ   r*   r*   r+   rC   ‹   rL   rM   Ú
board_tempc                 S   s    g | ]}|  d ¡r|  d ¡‘qS )Útempr>   r@   r*   r*   r+   Ú
<listcomp>�   s     z2_reconcile_whatsminer_firmware.<locals>.<listcomp>)	r?   r8   ÚsumÚintr3   r4   r5   ÚmaxÚpop)ÚresultÚboardsrB   ÚkÚrtÚavÚboard_tempsr*   r*   r+   Ú_reconcile_whatsminer_firmware_   sF   	€€ÿÿr[   c                 C   sn  d| dœ}zçt | dƒ}|rë| d¡}t|tƒr$t|ƒdkr$|d  d¡}|dkrë| d¡}t|tƒr5|}n	| di g¡d }| d	¡d
urJ| d	¡n| d¡|d< | d¡|d< | d¡|d< | d¡|d< | d¡|d< | d¡|d< | d¡|d< | d¡|d< | d¡|d< | d¡|d< | d¡|d < | d!¡|d"< | d#¡|d$< | d%¡|d&< | d'¡|d(< | d)¡|d*< | d+¡|d,< | d-¡|d.< | d/¡|d0< | d1¡|d2< | d3¡|d4< | d5¡|d6< | d7¡|d8< W n t�y } zt|ƒ|d9< W Y d
}~nd
}~ww z-t | d:ƒ}|�r2| d¡dk�r2| di ¡}| d;¡|d;< | d<¡|d=< | d>¡|d>< W n t�yL } zt|ƒ|d?< W Y d
}~nd
}~ww z,t | d@ƒ}|�rx| d¡dk�rx| di ¡}| dA¡|dB< | dC¡�rx| dC¡|dD< W n t�y’ } zt|ƒ|dE< W Y d
}~nd
}~ww z3t| dFƒ}|�rÅdGdH„ | dIg ¡D ƒ}| 	dJ¡ |�rÅt|ƒdKk�r½dL 
t|ƒ¡ntt|ƒƒ|dD< W n t�yß } zt|ƒ|dM< W Y d
}~nd
}~ww | dD¡�rø|dD  dL¡d  dN¡d  ¡ |dO< zgt | dPƒ}|�r^| d¡dk�r^| di ¡}t|tƒ�r^| dQdR¡ ¡ dSk|dT< | dU¡|dV< | dB¡�s5| dW¡|dB< | d¡d
u �r^zt| dX¡ƒ}|dYk�rLd
n||d< W n ttf�y]   Y nw W n t�yx } zt|ƒ|dZ< W Y d
}~nd
}~ww z.t | d[ƒ}|�r¦| d¡dk�r¦| di ¡}t|tƒ�r¦| d\g ¡}	t|	tƒ�r¦|	|d]< W n t�yÀ } zt|ƒ|d^< W Y d
}~nd
}~ww zxt | d_ƒ}|�r8| d¡}t|tƒ�rãt|ƒdk�rã|d  d¡}|dk�r8g }
| d`g ¡D ]C}|
 | da¡| db¡| dc¡ddk| de¡| d	¡| d¡| d¡| d!¡| d#¡| d%¡| d'¡| df¡| dg¡| dh¡diœ¡ �qð|
|dj< W n t�yR } zt|ƒ|dk< W Y d
}~nd
}~ww zGt | dlƒ}|�r™| d¡dk�r™| di ¡}t|tƒ�r™| dm¡�pz| dD¡|dn< | do¡|dp< | dq¡|dr< | ds¡|dt< | du¡|dv< W n t�y³ } zt|ƒ|dw< W Y d
}~nd
}~ww zct | dxƒ}|�r| d¡}t|tƒ�rÖt|ƒdk�rÖ|d  d¡}|dk�rg }| dyg ¡D ].}| dzdJ¡}|�rd{|v�r| || d|dJ¡| dedJ¡| d/¡| d1¡| d}¡d~œ¡ �qã||dx< W n t�y0 } zt|ƒ|d< W Y d
}~nd
}~ww t|ƒ |S )€zKCollect diagnostics from a Whatsminer using unencrypted read-only commands.Ú
whatsminer)Úfirmware_typer#   ÚsummaryÚSTATUSr   ÚSÚMsgÚSUMMARYzMHS 5sNzHS RTr:   zMHS avr;   zMHS 1mÚhashrate_mhs_1mzMHS 15mÚhashrate_mhs_15mz
Target MHSÚtarget_hashrate_mhsÚfreq_avgÚ	frequencyzTarget FreqÚtarget_frequencyÚPowerrE   zPower LimitÚpower_limitz
Power ModeÚ
power_modez
Power RateÚ
efficiencyÚTemperaturerN   zChip Temp MinÚchip_temp_minzChip Temp MaxÚchip_temp_maxzChip Temp AvgÚchip_temp_avgzEnv TempÚenv_tempzFan Speed InÚfan_inzFan Speed OutÚfan_outÚAcceptedrG   ÚRejectedrM   zPool Rejected%Úpool_rejected_pctzPool Stale%Úpool_stale_pctÚElapsedÚuptime_secondsÚsummary_errorÚget_miner_infoÚmacÚminersnÚserialÚhostnameÚminer_info_errorÚget_versionÚfw_verÚfirmwareÚ
miner_typeÚmodelÚversion_errorÚ
devdetailsc                 S   s"   h | ]}t |tƒr| d d¡’qS )ÚModelr   )Ú
isinstanceÚdictr?   r@   r*   r*   r+   Ú	<setcomp>î   s    ÿz%collect_whatsminer.<locals>.<setcomp>Ú
DEVDETAILSr   r   ú|Údevdetails_errorÚ_Úmodel_familyÚstatusÚmineroffÚfalseÚtrueÚis_sleepingÚmineroff_reasonÚsleep_reasonÚFirmwareVersionÚpower_limit_setiÿÿÿÚstatus_errorÚget_error_codeÚ
error_codeÚerror_codesÚerror_code_errorÚdevsÚDEVSÚASCÚSlotÚEnabledÚYÚStatuszChip FrequencyzEffective ChipszPCB SN)ÚindexÚslotÚenabledr‘   r:   r;   r<   rO   rn   ro   rp   rg   Úchipsr~   r9   Ú
devs_errorÚget_psuÚnameÚ	psu_modelÚpinrF   ÚvinÚpsu_voltage_inÚ	fan_speedÚpsu_fan_speedÚtemp0Úpsu_tempÚ	psu_errorrH   ÚPOOLSÚURLz	127.0.0.1ÚUserÚStale)ÚurlÚuserr‘   rG   rM   ÚstaleÚpools_error)r2   r?   r‰   Úlistr   rŠ   Ú	ExceptionÚstrr,   ÚdiscardÚjoinÚsortedÚnextÚiterÚsplitÚupperÚlowerrR   r4   r5   Úappendr[   )r#   rU   Údatar‘   ÚmsgÚsÚeÚmodelsÚplimÚcodesr9   ÚdevrH   rK   rº   r*   r*   r+   Úcollect_whatsminer™   sx  þ




&€€ÿ
€€ÿ
€€ÿ

,€€ÿ$
ÿ€€ÿ
€€ÿ


ò€€ÿ
€€ÿ




ú€€€ÿrÒ   )Ú__doc__r   r   ÚloggingÚ	getLoggerÚ__name__Úloggerr   r   r,   r2   r8   r[   rÒ   r*   r*   r*   r+   Ú<module>   s   

: