import { getEnumMemberName } from "@zwave-js/shared" ; import { padStart } from "alcalzone-shared/strings" ; import { Powerlevel } from "../commandclass/PowerlevelCC" ; import type { LifelineHealthCheckResult , LifelineHealthCheckSummary , RouteHealthCheckResult , RouteHealthCheckSummary , } from "./Types" ; export const healthCheckTestFrameCount = 10 ; export function healthCheckRatingToWord ( rating ) { return rating >= 10 ? "perfect" : rating >= 6 ? "good" : rating >= 4 ? "acceptable" : rating >= 1 ? "bad" : "dead" ; } export function formatLifelineHealthCheckRound ( round , numRounds , result ) { const ret = [ `· round ${ padStart ( round . toString ( ) , Math . floor ( Math . log10 ( numRounds ) + 1 ) , " " ) } - rating: ${ result . rating } (${ healthCheckRatingToWord ( result . rating ) })` , `  failed pings → node:             ${ result . failedPingsNode }/${ healthCheckTestFrameCount }` , `  max. latency:                    ${ result . latency . toFixed ( 1 ) } ms` , result . routeChanges != undefined ? `  route changes:                   ${ result . routeChanges }` : "" , result . snrMargin != undefined ? `  SNR margin:                      ${ result . snrMargin } dBm` : "" , result . failedPingsController != undefined ? `  failed pings → controller:       ${ result . failedPingsController }/${ healthCheckTestFrameCount } at normal power` : result . minPowerlevel != undefined ? `  min. node powerlevel w/o errors: ${ getEnumMemberName ( Powerlevel , result . minPowerlevel ) }` : "" , ] . filter ( ( line ) => ! ! line ) . join ( "\n" ) ; return ret ; } export function formatLifelineHealthCheckSummary ( summary ) { return `
rating:                   ${ summary . rating } (${ healthCheckRatingToWord ( summary . rating ) })
no. of routing neighbors: ${ summary . results [ summary . results . length - 1 ] . numNeighbors }
 
Check rounds:
${ summary . results . map ( ( r , i ) => formatLifelineHealthCheckRound ( i + 1 , summary . results . length , r ) ) . join ( "\n \n" ) }` . trim ( ) ; } export function formatRouteHealthCheckRound ( sourceNodeId , targetNodeId , round , numRounds , result ) { const ret = [ `· round ${ padStart ( round . toString ( ) , Math . floor ( Math . log10 ( numRounds ) + 1 ) , " " ) } - rating: ${ result . rating } (${ healthCheckRatingToWord ( result . rating ) })` , result . failedPingsToTarget != undefined ? `  failed pings ${ sourceNodeId } → ${ targetNodeId }:      ${ result . failedPingsToTarget }/${ healthCheckTestFrameCount }` : result . minPowerlevelSource != undefined ? `  Node ${ sourceNodeId } min. powerlevel w/o errors: ${ getEnumMemberName ( Powerlevel , result . minPowerlevelSource ) }` : "" , result . failedPingsToSource != undefined ? `  failed pings ${ targetNodeId } → ${ sourceNodeId }:      ${ result . failedPingsToSource }/${ healthCheckTestFrameCount }` : result . minPowerlevelTarget != undefined ? `  Node ${ targetNodeId } min. powerlevel w/o errors: ${ getEnumMemberName ( Powerlevel , result . minPowerlevelTarget ) }` : "" , ] . filter ( ( line ) => ! ! line ) . join ( "\n" ) ; return ret ; } export function formatRouteHealthCheckSummary ( sourceNodeId , targetNodeId , summary ) { return `
rating:                   ${ summary . rating } (${ healthCheckRatingToWord ( summary . rating ) })
no. of routing neighbors: ${ summary . results [ summary . results . length - 1 ] . numNeighbors }
 
Check rounds:
${ summary . results . map ( ( r , i ) => formatRouteHealthCheckRound ( sourceNodeId , targetNodeId , i + 1 , summary . results . length , r ) ) . join ( "\n \n" ) }` . trim ( ) ; }