github.com/simpleiot/simpleiot@v0.18.3/frontend/src/Components/NodeSerialDev.elm (about) 1 module Components.NodeSerialDev exposing (view) 2 3 import Api.Node as Node 4 import Api.Point as Point 5 import Components.NodeOptions exposing (NodeOptions, oToInputO) 6 import Dict exposing (Dict) 7 import Element exposing (..) 8 import Element.Background as Background 9 import Element.Border as Border 10 import Element.Font as Font 11 import FormatNumber exposing (format) 12 import FormatNumber.Locales exposing (Decimals(..), usLocale) 13 import Round 14 import Time 15 import UI.Icon as Icon 16 import UI.NodeInputs as NodeInputs 17 import UI.Style as Style 18 import UI.ViewIf exposing (viewIf) 19 import Utils.Iso8601 exposing (toDateTimeString) 20 21 22 horizontalRule : Element msg 23 horizontalRule = 24 el 25 [ width fill 26 , height (px 1) 27 , Border.color (Element.rgb 0.5 0.5 0.5) 28 , Border.widthEach { bottom = 2, top = 0, left = 0, right = 0 } 29 ] 30 Element.none 31 32 33 view : NodeOptions msg -> Element msg 34 view o = 35 let 36 disabled = 37 Point.getBool o.node.points Point.typeDisabled "" 38 39 connected = 40 Point.getBool o.node.points Point.typeConnected "" 41 42 summaryBackground = 43 if disabled || not connected then 44 Style.colors.ltgray 45 46 else 47 Style.colors.none 48 in 49 column 50 [ width fill 51 , Border.widthEach { top = 2, bottom = 0, left = 0, right = 0 } 52 , Border.color Style.colors.black 53 , spacing 6 54 ] 55 <| 56 wrappedRow [ spacing 10, Background.color summaryBackground ] 57 [ Icon.serialDev 58 , text <| 59 Point.getText o.node.points Point.typeDescription "" 60 , viewIf disabled <| text "(disabled)" 61 , viewIf (not connected) <| text "(not connected)" 62 ] 63 :: (if o.expDetail then 64 let 65 labelWidth = 66 180 67 68 opts = 69 oToInputO o labelWidth 70 71 textInput = 72 NodeInputs.nodeTextInput opts "0" 73 74 numberInput = 75 NodeInputs.nodeNumberInput opts "0" 76 77 counterWithReset = 78 NodeInputs.nodeCounterWithReset opts "0" 79 80 checkboxInput = 81 NodeInputs.nodeCheckboxInput opts "0" 82 83 optionInput = 84 NodeInputs.nodeOptionInput opts "0" 85 86 button = 87 NodeInputs.nodeButtonActionText opts "0" 88 89 log = 90 Point.getText o.node.points Point.typeLog "0" 91 92 rate = 93 Point.getValue o.node.points Point.typeRate "0" 94 95 rateS = 96 String.fromFloat (Round.roundNum 0 rate) 97 98 rateHR = 99 Point.getValue o.node.points Point.typeRateHR "0" 100 101 rateHRS = 102 String.fromFloat (Round.roundNum 0 rateHR) 103 104 download = 105 Point.getText o.node.points Point.typeDownload "0" 106 107 downloading = 108 String.length download > 0 109 110 progress = 111 round <| Point.getValue o.node.points Point.typeProgress "0" 112 113 files = 114 List.filter 115 (\n -> 116 n.node.typ == Node.typeFile 117 ) 118 o.children 119 in 120 [ textInput Point.typeDescription "Description" "" 121 , textInput Point.typePort "Port" "/dev/ttyUSB0" 122 , textInput Point.typeBaud "Baud" "9600" 123 , numberInput Point.typeMaxMessageLength "Max Msg Len" 124 , textInput Point.typeHRDest "HR Dest Node" "" 125 , checkboxInput Point.typeSyncParent "Sync parent node" 126 , numberInput Point.typeDebug "Debug level (0-9)" 127 , checkboxInput Point.typeDisabled "Disabled" 128 , horizontalRule 129 , counterWithReset Point.typeErrorCount Point.typeErrorCountReset "Error Count" 130 , counterWithReset Point.typeRx Point.typeRxReset "Rx count" 131 , counterWithReset Point.typeTx Point.typeTxReset "Tx count" 132 , counterWithReset Point.typeErrorCountHR Point.typeErrorCountResetHR "HR err count" 133 , counterWithReset Point.typeHrRx Point.typeHrRxReset "HR Rx count" 134 , text <| " Rate (pts/sec): " ++ rateS 135 , text <| " Rate HR (pkts/sec): " ++ rateHRS 136 , text <| " Last log: " ++ log 137 , viewIf (List.length files > 0) <| horizontalRule 138 , viewIf (List.length files > 0 && not downloading) <| 139 optionInput Point.typeDownload 140 "Download file" 141 (List.map 142 (\n -> 143 let 144 description = 145 Point.getText n.node.points Point.typeDescription "0" 146 147 filename = 148 Point.getText n.node.points Point.typeName "0" 149 in 150 ( filename, description ++ ": " ++ filename ) 151 ) 152 files 153 ) 154 , viewIf downloading <| 155 text <| 156 "Downloading: " 157 ++ download 158 , viewIf downloading <| 159 text <| 160 "Progress: " 161 ++ String.fromInt progress 162 ++ " %" 163 , viewIf downloading <| 164 button Point.typeDownload "" "Cancel download" Style.colors.red 165 , horizontalRule 166 , viewPoints o.zone <| Point.filterSpecialPoints <| List.sortWith Point.sort o.node.points 167 , NodeInputs.nodeKeyValueInput opts Point.typeTag "Tags" "Add Tag" 168 ] 169 170 else 171 [] 172 ) 173 174 175 viewPoints : Time.Zone -> List Point.Point -> Element msg 176 viewPoints z pts = 177 let 178 formaters = 179 metricFormaters z 180 181 fm = 182 formatMetric formaters 183 in 184 table [ padding 7 ] 185 { data = Point.filterTombstone pts |> List.map fm 186 , columns = 187 let 188 cell = 189 el [ paddingXY 15 5, Border.width 1 ] 190 in 191 [ { header = cell <| el [ Font.bold, centerX ] <| text "Point" 192 , width = fill 193 , view = \m -> cell <| text m.desc 194 } 195 , { header = cell <| el [ Font.bold, centerX ] <| text "Value" 196 , width = fill 197 , view = \m -> cell <| el [ alignRight ] <| text m.value 198 } 199 ] 200 } 201 202 203 formatMetric : Dict String MetricFormat -> Point.Point -> { desc : String, value : String } 204 formatMetric formaters p = 205 case Dict.get p.typ formaters of 206 Just f -> 207 { desc = f.desc p, value = f.vf p } 208 209 Nothing -> 210 Point.renderPoint2 p 211 212 213 type alias MetricFormat = 214 { desc : Point.Point -> String 215 , vf : Point.Point -> String 216 } 217 218 219 metricFormaters : Time.Zone -> Dict String MetricFormat 220 metricFormaters z = 221 let 222 toTimeWithZone = 223 toTime z 224 in 225 Dict.fromList 226 [ ( "metricAppAlloc", { desc = descS "App Memory Alloc", vf = toMiB } ) 227 , ( "metricAppNumGoroutine", { desc = descS "App Goroutine Count", vf = toWhole } ) 228 , ( "metricProcCPUPercent", { desc = descS "Proc CPU %", vf = toPercent } ) 229 , ( "metricProcMemPercent", { desc = descS "Proc Mem %", vf = toPercent } ) 230 , ( "metricProcMemRSS", { desc = descS "Proc Mem RSS", vf = toMiB } ) 231 , ( "host", { desc = descKey "Host", vf = toText } ) 232 , ( "hostBootTime", { desc = descS "Host Boot Time", vf = toTimeWithZone } ) 233 , ( "metricSysCPUPercent", { desc = descS "Sys CPU %", vf = toPercent } ) 234 , ( "metricSysDiskUsedPercent", { desc = descKey "Disk Used %", vf = toPercent } ) 235 , ( "metricSysLoad", { desc = descKey "Load", vf = \p -> Round.round 2 p.value } ) 236 , ( "metricSysMemUsedPercent", { desc = descS "Memory used %", vf = toPercent } ) 237 , ( "metricSysMem", { desc = descKey "Memory", vf = toMiB } ) 238 , ( "metricSysNetBytesRecv", { desc = descKey "Net RX", vf = toWhole } ) 239 , ( "metricSysNetBytesSent", { desc = descKey "Net TX", vf = toWhole } ) 240 , ( "metricSysUptime", { desc = descKey "Uptime", vf = toWhole } ) 241 ] 242 243 244 toMiB : Point.Point -> String 245 toMiB p = 246 format { usLocale | decimals = Exact 1 } (p.value / (1024 * 1024)) 247 248 249 toPercent : Point.Point -> String 250 toPercent p = 251 Round.round 1 p.value ++ " %" 252 253 254 toWhole : Point.Point -> String 255 toWhole p = 256 format { usLocale | decimals = Exact 0 } p.value 257 258 259 toText : Point.Point -> String 260 toText p = 261 if p.text == "" then 262 " " 263 264 else 265 p.text 266 267 268 toTime : Time.Zone -> Point.Point -> String 269 toTime z p = 270 let 271 t = 272 Time.millisToPosix (round p.value * 1000) 273 in 274 toDateTimeString z t 275 276 277 descS : String -> Point.Point -> String 278 descS d _ = 279 d 280 281 282 descKey : String -> Point.Point -> String 283 descKey d p = 284 d ++ " " ++ p.key