feat(ui): display supervision url in charging stations list
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 9 Feb 2024 19:42:02 +0000 (20:42 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 9 Feb 2024 19:42:02 +0000 (20:42 +0100)
closes #967

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
13 files changed:
src/charging-station/ChargingStation.ts
src/types/ChargingStationWorker.ts
src/utils/MessageChannelUtils.ts
ui/web/src/App.vue
ui/web/src/components/Container.vue
ui/web/src/components/charging-stations/CSConnector.vue
ui/web/src/components/charging-stations/CSData.vue
ui/web/src/components/charging-stations/CSInfoModal.vue
ui/web/src/components/charging-stations/CSTable.vue
ui/web/src/composables/Utils.ts
ui/web/src/types/ChargingStationType.ts
ui/web/src/views/ChargingStationsView.vue
ui/web/tests/unit/CSTable.spec.ts

index a086771934e14d564477f3dd80b6ed1b3854fea7..8bbf7848db415324ece01a8441af474218b998c0 100644 (file)
@@ -260,7 +260,7 @@ export class ChargingStation extends EventEmitter {
     return this.connectors.size === 0 && this.evses.size > 0
   }
 
-  private get wsConnectionUrl (): URL {
+  public get wsConnectionUrl (): URL {
     return new URL(
       `${
         this.stationInfo?.supervisionUrlOcppConfiguration === true &&
@@ -801,14 +801,12 @@ export class ChargingStation extends EventEmitter {
 
     if (this.isWebSocketConnectionOpened()) {
       logger.warn(
-        `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.toString()} is already opened`
+        `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.href} is already opened`
       )
       return
     }
 
-    logger.info(
-      `${this.logPrefix()} Open OCPP connection to URL ${this.wsConnectionUrl.toString()}`
-    )
+    logger.info(`${this.logPrefix()} Open OCPP connection to URL ${this.wsConnectionUrl.href}`)
 
     this.wsConnection = new WebSocket(
       this.wsConnectionUrl,
@@ -1802,7 +1800,7 @@ export class ChargingStation extends EventEmitter {
   private async onOpen (): Promise<void> {
     if (this.isWebSocketConnectionOpened()) {
       logger.info(
-        `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} succeeded`
+        `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.href} succeeded`
       )
       let registrationRetryCount = 0
       if (!this.isRegistered()) {
@@ -1856,7 +1854,7 @@ export class ChargingStation extends EventEmitter {
       this.emit(ChargingStationEvents.updated)
     } else {
       logger.warn(
-        `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} failed`
+        `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.href} failed`
       )
     }
   }
index df4a789c55f69f2f26ff259d11b787c4b534935e..d0cdf900dd2a836ed76dd827c035e795714757e2 100644 (file)
@@ -31,6 +31,7 @@ export interface ChargingStationData extends WorkerData {
   connectors: ConnectorStatus[]
   evses: EvseStatusWorkerType[]
   ocppConfiguration: ChargingStationOcppConfiguration
+  supervisionUrl: string
   wsState?:
   | typeof WebSocket.CONNECTING
   | typeof WebSocket.OPEN
index 212ed5f20124c419fbf3933b6e2855dfcb482a07..a3b267eee84aa82ccd30f106f2a5119b323d9d0b 100644 (file)
@@ -68,6 +68,7 @@ export const buildChargingStationDataPayload = (
     evses: buildEvsesStatus(chargingStation, OutputFormat.worker),
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     ocppConfiguration: chargingStation.ocppConfiguration!,
+    supervisionUrl: chargingStation.wsConnectionUrl.href,
     wsState: chargingStation.wsConnection?.readyState,
     bootNotificationResponse: chargingStation.bootNotificationResponse,
     ...(chargingStation.automaticTransactionGenerator != null && {
index d6df1a214e195080b4f74d25530910181ea9aef4..87c7aa45293f9c1282cde9c51cfd38a0576d17bc 100644 (file)
@@ -4,8 +4,8 @@
 
 <style>
 #app {
-  height: 100%;
-  width: 100%;
+  height: fit-content;
+  width: fit-content;
   font-family: Avenir, Helvetica, Arial, sans-serif;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
 }
 
 body {
-  margin: 0;
-  padding: 0;
-  height: 100vh;
-  width: 100vw;
+  height: fit-content;
+  width: fit-content;
+  margin: 0.05%;
+  padding: 0.05%;
   background-color: rgb(233, 227, 227);
 }
 </style>
index c9683bbe76199c79ea5f3b4847c53fcfcbed9e60..451a7659d45e73807f69d596d417db6f0156fd28 100644 (file)
@@ -6,9 +6,6 @@
 
 <style>
 .container {
-  display: flex;
-  box-sizing: border-box;
-  max-height: 100%;
-  max-width: 100%;
+  flex: auto;
 }
 </style>
index d828ae8620e0d3874a83b62b7f4421f838f82227..29bb07185804be2e641a934c2df1994447d85495 100644 (file)
@@ -1,5 +1,5 @@
 <template>
-  <td class="cs-table__action-col">
+  <td class="cs-table__column">
     <Button @click="startChargingStation()">Start Charging Station</Button>
     <Button @click="stopChargingStation()">Stop Charging Station</Button>
     <Button @click="openConnection()">Open Connection</Button>
@@ -17,9 +17,9 @@
     <Button @click="startAutomaticTransactionGenerator()">Start ATG</Button>
     <Button @click="stopAutomaticTransactionGenerator()">Stop ATG</Button>
   </td>
-  <td class="cs-table__connector-col">{{ connectorId }}</td>
-  <td class="cs-table__status-col">{{ connector.status }}</td>
-  <td class="cs-table__transaction-col">{{ connector.transactionStarted ? 'Yes' : 'No' }}</td>
+  <td class="cs-table__column">{{ connectorId }}</td>
+  <td class="cs-table__column">{{ connector.status ?? 'Ø' }}</td>
+  <td class="cs-table__column">{{ connector.transactionStarted === true ? 'Yes' : 'No' }}</td>
 </template>
 
 <script setup lang="ts">
index 1b740f750f1b5cc558e91a3e75e69c597331e758..befdbb4b8b71d626a0bc636ece3fb40818ad3aca 100644 (file)
@@ -7,14 +7,15 @@
       :transaction-id="connector.transactionId"
       :id-tag="props.idTag"
     />
-    <td class="cs-table__name-col">{{ getId() }}</td>
-    <td class="cs-table__started-col">{{ getStarted() }}</td>
-    <td class="cs-table__wsState-col">{{ getWsState() }}</td>
-    <td class="cs-table__registration-status-col">{{ getRegistrationStatus() }}</td>
-    <td class="cs-table__template-col">{{ getInfo().templateName }}</td>
-    <td class="cs-table__vendor-col">{{ getVendor() }}</td>
-    <td class="cs-table__model-col">{{ getModel() }}</td>
-    <td class="cs-table__firmware-col">{{ getFirmwareVersion() }}</td>
+    <td class="cs-table__column">{{ getId() }}</td>
+    <td class="cs-table__column">{{ getStarted() }}</td>
+    <td class="cs-table__column">{{ getSupervisionUrl() }}</td>
+    <td class="cs-table__column">{{ getWsState() }}</td>
+    <td class="cs-table__column">{{ getRegistrationStatus() }}</td>
+    <td class="cs-table__column">{{ getInfo().templateName }}</td>
+    <td class="cs-table__column">{{ getVendor() }}</td>
+    <td class="cs-table__column">{{ getModel() }}</td>
+    <td class="cs-table__column">{{ getFirmwareVersion() }}</td>
   </tr>
 </template>
 
@@ -22,7 +23,6 @@
 // import { reactive } from 'vue'
 import CSConnector from './CSConnector.vue'
 import type { ChargingStationData, ChargingStationInfo, ConnectorStatus } from '@/types'
-import { ifUndefined } from '@/composables/Utils'
 
 const props = defineProps<{
   chargingStation: ChargingStationData
@@ -60,7 +60,7 @@ function getHashId(): string {
   return getInfo().hashId
 }
 function getId(): string {
-  return ifUndefined<string>(getInfo().chargingStationId, 'Ø')
+  return getInfo().chargingStationId ?? 'Ø'
 }
 function getModel(): string {
   return getInfo().chargePointModel
@@ -69,11 +69,15 @@ function getVendor(): string {
   return getInfo().chargePointVendor
 }
 function getFirmwareVersion(): string {
-  return ifUndefined<string>(getInfo().firmwareVersion, 'Ø')
+  return getInfo().firmwareVersion ?? 'Ø'
 }
 function getStarted(): string {
   return props.chargingStation.started === true ? 'Yes' : 'No'
 }
+function getSupervisionUrl(): string {
+  const supervisionUrl = new URL(props.chargingStation.supervisionUrl)
+  return `${supervisionUrl.protocol}//${supervisionUrl.host.split('.').join('.\u200b')}`
+}
 function getWsState(): string {
   switch (props.chargingStation?.wsState) {
     case WebSocket.CONNECTING:
index 8afb2de1a5b04fdfcc143963ae499ebb226c657c..05b4b6bdbbd0f6691215c4dc270e92a1e1f9a03b 100644 (file)
@@ -17,6 +17,5 @@ const props = defineProps<{
 <style>
 .card-info {
   background-color: white;
-  padding: 0.2%;
 }
 </style>
index 11dfb5bb286923fa9feccc03d85cc185ebd26307..6fcf74683384943a8370ad6767adec8ca196ebc4 100644 (file)
@@ -5,18 +5,19 @@
     </caption>
     <thead id="cs-table__head">
       <tr class="cs-table__row">
-        <th scope="col" class="cs-table__action-col">Action</th>
-        <th scope="col" class="cs-table__connector-col">Connector</th>
-        <th scope="col" class="cs-table__status-col">Status</th>
-        <th scope="col" class="cs-table__transaction-col">Transaction</th>
-        <th scope="col" class="cs-table__name-col">Name</th>
-        <th scope="col" class="cs-table__started-col">Started</th>
-        <th scope="col" class="cs-table__wsState-col">WebSocket State</th>
-        <th scope="col" class="cs-table__registration-status-col">Registration Status</th>
-        <th scope="col" class="cs-table__template-col">Template</th>
-        <th scope="col" class="cs-table__vendor-col">Vendor</th>
-        <th scope="col" class="cs-table__model-col">Model</th>
-        <th scope="col" class="cs-table__firmware-col">Firmware Version</th>
+        <th scope="col" class="cs-table__column">Action</th>
+        <th scope="col" class="cs-table__column">Connector</th>
+        <th scope="col" class="cs-table__column">Status</th>
+        <th scope="col" class="cs-table__column">Transaction</th>
+        <th scope="col" class="cs-table__column">Name</th>
+        <th scope="col" class="cs-table__column">Started</th>
+        <th scope="col" class="cs-table__column">Supervision Url</th>
+        <th scope="col" class="cs-table__column">WebSocket State</th>
+        <th scope="col" class="cs-table__column">Registration Status</th>
+        <th scope="col" class="cs-table__column">Template</th>
+        <th scope="col" class="cs-table__column">Vendor</th>
+        <th scope="col" class="cs-table__column">Model</th>
+        <th scope="col" class="cs-table__column">Firmware Version</th>
       </tr>
     </thead>
     <tbody id="cs-table__body">
@@ -42,11 +43,10 @@ const props = defineProps<{
 
 <style>
 #cs-table {
-  height: 100%;
+  height: fit-content;
   width: 100%;
   background-color: white;
   display: flex;
-  flex-grow: 1;
   flex-direction: column;
   overflow: auto hidden;
   border-collapse: collapse;
@@ -57,16 +57,16 @@ const props = defineProps<{
 #cs-table__body {
   height: fit-content;
   width: 100%;
-  min-width: 100%;
-  display: block;
+  display: flex;
+  flex-direction: column;
 }
 
 #cs-table__body {
   overflow: visible overlay;
-  flex-grow: 1;
 }
 
 .cs-table__row {
+  height: fit-content;
   width: 100%;
   display: flex;
   justify-content: center;
@@ -81,22 +81,9 @@ const props = defineProps<{
   background-color: rgb(223, 217, 217);
 }
 
-.cs-table__action-col,
-.cs-table__connector-col,
-.cs-table__status-col,
-.cs-table__transaction-col,
-.cs-table__name-col,
-.cs-table__started-col,
-.cs-table__wsState-col,
-.cs-table__registration-status-col,
-.cs-table__template-col,
-.cs-table__model-col,
-.cs-table__vendor-col,
-.cs-table__firmware-col {
-  height: 0.1%;
-  width: 20%;
-  padding-top: 0.2%;
-  padding-bottom: 0.2%;
+.cs-table__column {
+  height: fit-content;
+  width: calc(100% / 13);
   text-align: center;
 }
 </style>
index a9eb33b3277cbba434c0d7c671fb76834985b6ff..2f9c1ac5e92e669e7113813a378da986f8f11236 100644 (file)
@@ -1,8 +1,3 @@
-export const ifUndefined = <T>(value: T | undefined, isValue: T): T => {
-  if (value === undefined) return isValue
-  return value as T
-}
-
 // export const compose = <T>(...fns: ((arg: T) => T)[]): ((x: T) => T) => {
 //   return (x: T) => fns.reduceRight((y, fn) => fn(y), x)
 // }
index 7e8457a9e8df6bc9a2fda96eee4d0670ae9d5aeb..276e9dd9f159b1810051a107f34552960fa4641b 100644 (file)
@@ -6,6 +6,7 @@ export type ChargingStationData = {
   connectors: ConnectorStatus[]
   evses: EvseStatus[]
   ocppConfiguration: ChargingStationOcppConfiguration
+  supervisionUrl: string
   wsState?:
     | typeof WebSocket.CONNECTING
     | typeof WebSocket.OPEN
index 4e4772ecbf22c84a153471243d86c0280d5b16c8..f500591a551c2c5332add353dded21d7647ddc5a 100644 (file)
@@ -61,9 +61,8 @@ function stopSimulator(): void {
 
 <style>
 #charging-stations {
-  height: 100%;
+  height: fit-content;
   width: 100%;
-  padding: 0.5%;
   background-color: rgb(233, 227, 227);
   display: flex;
   flex-direction: column;
@@ -96,5 +95,6 @@ function stopSimulator(): void {
 
 #idtag-field {
   flex: auto;
+  text-align: center;
 }
 </style>
index d27387480fdbe0f93991538292b84ebc854fd18d..a756a6e5a24b682721b0e12bd11d78a60256d0fe 100644 (file)
@@ -14,6 +14,7 @@ test('renders CS table columns name', () => {
   expect(wrapper.text()).to.include('Transaction')
   expect(wrapper.text()).to.include('Name')
   expect(wrapper.text()).to.include('Started')
+  expect(wrapper.text()).to.include('Supervision Url')
   expect(wrapper.text()).to.include('WebSocket State')
   expect(wrapper.text()).to.include('Registration Status')
   expect(wrapper.text()).to.include('Template')