diff --git a/hardware/pcb/CybICS.kicad_pcb b/hardware/pcb/CybICS.kicad_pcb index 3d78131..2f4245d 100644 --- a/hardware/pcb/CybICS.kicad_pcb +++ b/hardware/pcb/CybICS.kicad_pcb @@ -14711,7 +14711,7 @@ ) (fp_arc (start 64.20104 22.49932) - (mid 64.054479 22.85315) + (mid 64.054473 22.853115) (end 63.70066 22.9997) (stroke (width 0.12) diff --git a/hardware/pcb/README.md b/hardware/pcb/README.md new file mode 100644 index 0000000..185feb4 --- /dev/null +++ b/hardware/pcb/README.md @@ -0,0 +1,23 @@ +### Ordering PCB at JLCPCB + +Open KiCad and under "Tools" → "Plugin and Content Manager": +
+ +
+ +Install the "Fabrication Toolkit": +
+ +
+ +Open the "PCB Editor" and click on the icon of "Fabrication Toolkit": +
+ +
+ +Generate the fabrication data: +
+ +
+ +Now you have a folder "production", with all the file to upload to JLCPCB. diff --git a/hardware/pcb/doc/fabricationToolkit.png b/hardware/pcb/doc/fabricationToolkit.png new file mode 100644 index 0000000..7728eb1 Binary files /dev/null and b/hardware/pcb/doc/fabricationToolkit.png differ diff --git a/hardware/pcb/doc/generate.png b/hardware/pcb/doc/generate.png new file mode 100644 index 0000000..c990f38 Binary files /dev/null and b/hardware/pcb/doc/generate.png differ diff --git a/hardware/pcb/doc/pcbEditor.png b/hardware/pcb/doc/pcbEditor.png new file mode 100644 index 0000000..e5c3ee7 Binary files /dev/null and b/hardware/pcb/doc/pcbEditor.png differ diff --git a/hardware/pcb/doc/pluginManager.png b/hardware/pcb/doc/pluginManager.png new file mode 100644 index 0000000..c0c7290 Binary files /dev/null and b/hardware/pcb/doc/pluginManager.png differ diff --git a/hardware/pcb/docs/CybICS-board_bottom.pdf b/hardware/pcb/docs/CybICS-board_bottom.pdf index 1e79986..1f64360 100644 Binary files a/hardware/pcb/docs/CybICS-board_bottom.pdf and b/hardware/pcb/docs/CybICS-board_bottom.pdf differ diff --git a/hardware/pcb/docs/CybICS-board_top.pdf b/hardware/pcb/docs/CybICS-board_top.pdf index 2bbec8a..f440462 100644 Binary files a/hardware/pcb/docs/CybICS-board_top.pdf and b/hardware/pcb/docs/CybICS-board_top.pdf differ diff --git a/hardware/pcb/docs/CybICS-schematic.pdf b/hardware/pcb/docs/CybICS-schematic.pdf index 13851a4..077a2af 100644 Binary files a/hardware/pcb/docs/CybICS-schematic.pdf and b/hardware/pcb/docs/CybICS-schematic.pdf differ diff --git a/hardware/pcb/pcb/CybICS-top.svg b/hardware/pcb/pcb/CybICS-top.svg index 03899b9..5c7957f 100644 --- a/hardware/pcb/pcb/CybICS-top.svg +++ b/hardware/pcb/pcb/CybICS-top.svg @@ -8959,7 +8959,7 @@ - + diff --git a/hardware/pcb/pcb/CybICS_top.png b/hardware/pcb/pcb/CybICS_top.png index 4cb8ae6..e80e0cc 100644 Binary files a/hardware/pcb/pcb/CybICS_top.png and b/hardware/pcb/pcb/CybICS_top.png differ diff --git a/software/FUXA/Dockerfile b/software/FUXA/Dockerfile index ecbcf85..479622c 100644 --- a/software/FUXA/Dockerfile +++ b/software/FUXA/Dockerfile @@ -3,6 +3,7 @@ FROM debian:12 RUN apt-get update && apt-get install -y \ npm \ curl \ + unixodbc-dev \ && rm -rf /var/lib/apt/lists/* RUN npm install -g --unsafe-perm @frangoteam/fuxa diff --git a/software/hwio-virtual/Dockerfile b/software/hwio-virtual/Dockerfile index 6fc1099..7c65d53 100644 --- a/software/hwio-virtual/Dockerfile +++ b/software/hwio-virtual/Dockerfile @@ -3,8 +3,10 @@ FROM python:3 WORKDIR /CybICS COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt +RUN mkdir -p pics COPY hardwareAbstraction.py ./ -COPY CybICS_logo.png ./ -COPY favicon.ico ./ +COPY pics/CybICS_logo.png pics/CybICS_logo.png +COPY pics/favicon.ico pics/favicon.ico +COPY pics/pcb.png pics/pcb.png CMD [ "python", "./hardwareAbstraction.py" ] diff --git a/software/hwio-virtual/hardwareAbstraction.py b/software/hwio-virtual/hardwareAbstraction.py index cf3530e..62ae47e 100644 --- a/software/hwio-virtual/hardwareAbstraction.py +++ b/software/hwio-virtual/hardwareAbstraction.py @@ -11,10 +11,32 @@ client = ModbusTcpClient(host="openplc",port=502) # Create client object client.connect() # connect to device, reconnect automatically -def thread_nicegui(): - ui.run(port=8090,reload=False,show=False,dark=True,favicon="favicon.ico",title="CybICS VIRT") +# Global variables +gst=0 +hpt=0 +sysSen=0 +boSen=0 +heartbeat=0 +compressor=0 +systemValve=0 +gstSig=0 +delay=0 +timer=0 -if __name__ == "__main__": +# definiton for button reset +def button_reset(): + # use global variables + global gst + global hpt + global sysSen + global boSen + global heartbeat + global compressor + global systemValve + global gstSig + global delay + global timer + logging.info("button_rest: clicked") gst=0 hpt=0 sysSen=0 @@ -24,6 +46,15 @@ def thread_nicegui(): systemValve=0 gstSig=0 delay=0 + timer=0 + logging.info("button_rest: all reseted") + +# thread for nicegui +def thread_nicegui(): + ui.run(port=8090,reload=False,show=False,dark=True,favicon="pics/favicon.ico",title="CybICS VIRT") + +# main function +if __name__ == "__main__": flag = [17273, 25161, 17235, 10349, 12388, 25205, 9257] format = "%(asctime)s: %(message)s" @@ -37,54 +68,186 @@ def thread_nicegui(): logging.info("Main : before running thread") x.start() - # NiceGUI setup - with ui.row(): - ui.spinner('dots', size='lg', color='red') - ui.image('CybICS_logo.png').classes('w-64') - ui.spinner('dots', size='lg', color='red') - - columns = [ - {'name': 'variable', 'label': 'Variable', 'field': 'variable', 'required': True, 'align': 'left'}, - {'name': 'value', 'label': 'Value', 'field': 'value', 'sortable': True}, - ] - rows = [ - {'variable': 'GST', 'value': gst}, - {'variable': 'HPT', 'value': hpt}, - {'variable': 'boSen', 'value': boSen}, - {'variable': 'heartbeat', 'value': heartbeat}, - {'variable': 'compressor', 'value': compressor}, - {'variable': 'systemValve', 'value': systemValve}, - {'variable': 'gstSig', 'value': gstSig}, - {'variable': 'System', 'value': sysSen}, - ] + # Create container for the content + with ui.element('div').style('text-align: center; min-width: 1024; width: 1024;'): + + # NiceGUI setup + with ui.row().style('width: 900px; text-align: center; align-items: center; justify-content: center;'): + ui.spinner('dots', size='lg', color='red') + ui.image('pics/CybICS_logo.png').classes('w-64') + ui.spinner('dots', size='lg', color='red') + + columns = [ + {'name': 'variable', 'label': 'Variable', 'field': 'variable', 'required': True, 'align': 'left'}, + {'name': 'value', 'label': 'Value', 'field': 'value', 'sortable': True}, + ] + rows = [ + {'variable': 'GST', 'value': gst}, + {'variable': 'HPT', 'value': hpt}, + {'variable': 'boSen', 'value': boSen}, + {'variable': 'heartbeat', 'value': heartbeat}, + {'variable': 'compressor', 'value': compressor}, + {'variable': 'systemValve', 'value': systemValve}, + {'variable': 'gstSig', 'value': gstSig}, + {'variable': 'System', 'value': sysSen}, + ] + # add horizontal line + with ui.element('div').style('display: flex; justify-content: center;'): + ui.element('div').style( + 'height: 1px; background-color: white; width: 80%; margin: 20px 0;' + ) + + # Add PCB as a PNG image + with ui.element('div').style('position: relative; display: inline-block;'): + # Display the PNG image + ui.image('pics/pcb.png').style('width: 100%; height: auto; display: block; width: 800px; height: 500px; margin: auto;') + + # Reset button + ui.button('reset', on_click=button_reset).style( + 'position: absolute; top: 240px; left: 0px;' + 'background-color: red; color: white; width: 40px; height: 5px;' + 'display: block;' + ) + + # Overlay Display + DISPLAYoverlay1 = ui.label('CybICS v1.0.0').style( + 'position: absolute; top: 370px; left: 430px; border-radius: 50%; color=black' + 'background-color: transparent; font-size: 40px;' + 'display: block;' + ) + DISPLAYoverlay2 = ui.label('0').style( + 'position: absolute; top: 415px; left: 430px; border-radius: 50%; color=black' + 'background-color: transparent; font-size: 40px; width:330px; text-align: right;' + 'display: block;' + ) - # Build a visual representation of the process - with ui.row(): - with ui.column(): - variableTable = ui.table(columns=columns, rows=rows, row_key='name') - with ui.column(): - ui.label('Gas Storage Tank (GST)') - with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as gstCard: - gstLabel = ui.label(str(gst)).style('color: black;') - ui.label('System Valve ') - with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as systemValveCard: - systemValveLabel = ui.label(str(systemValve)).style('color: black;') - with ui.column(): - ui.label('Compressor (C)') - with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as cCard: - cLabel = ui.label(str(compressor)).style('color: black;') - ui.label('System (Sys)') - with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as sysCard: - sysLabel = ui.label(str(sysSen)).style('color: black;') - with ui.column(): - ui.label('High Pressure Tank (HPT)') - with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as hptCard: - hptLabel = ui.label(str(hpt)).style('color: black;') - ui.label('Blow Out (BO)') - with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as boCard: - boLabel = ui.label(str(boSen)).style('color: black;') + # Overlay GST + GSToverlayLow=ui.card().style( + 'position: absolute; top: 140px; left: 115px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + GSToverlayNormal=ui.card().style( + 'position: absolute; top: 110px; left: 115px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + GSToverlayHigh=ui.card().style( + 'position: absolute; top: 80px; left: 115px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: block;' + ) + # Overlay Compressor + CoverlayOn=ui.card().style( + 'position: absolute; top: 95px; left: 355px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + CoverlayOff=ui.card().style( + 'position: absolute; top: 125px; left: 355px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + + # Overlay HPT + HPToverlayEmpty=ui.card().style( + 'position: absolute; top: 210px; left: 495px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: block;' + ) + HPToverlayLow=ui.card().style( + 'position: absolute; top: 184px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: block;' + ) + HPToverlayNormal=ui.card().style( + 'position: absolute; top: 158px; left: 495px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + HPToverlayHigh=ui.card().style( + 'position: absolute; top: 132px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: block;' + ) + HPToverlayCritical=ui.card().style( + 'position: absolute; top: 106px; left: 495px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + + # Overlay Blow Out + BOoverlayOpen=ui.card().style( + 'position: absolute; top: 55px; left: 680px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + BOoverlayClosed=ui.card().style( + 'position: absolute; top: 80px; left: 680px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + + # Overlay System + SoverlayWorking=ui.card().style( + 'position: absolute; top: 140px; left: 680px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + SoverlayError=ui.card().style( + 'position: absolute; top: 165px; left: 680px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + + # Overlay System Valve + SVoverlayOpen=ui.card().style( + 'position: absolute; top: 245px; left: 620px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + SVoverlayClosed=ui.card().style( + 'position: absolute; top: 270px; left: 620px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + + # add horizontal line + with ui.element('div').style('display: flex; justify-content: center;'): + ui.element('div').style( + 'height: 1px; background-color: white; width: 80%; margin: 20px 0;' + ) + + # Build a visual representation of the process + with ui.row().style('width: 900px; text-align: center; align-items: center; justify-content: center;'): + with ui.column(): + variableTable = ui.table(columns=columns, rows=rows, row_key='name') + with ui.column(): + ui.label('Gas Storage Tank (GST)') + with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as gstCard: + gstLabel = ui.label(str(gst)).style('color: black;') + ui.label('System Valve ') + with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as systemValveCard: + systemValveLabel = ui.label(str(systemValve)).style('color: black;') + with ui.column(): + ui.label('Compressor (C)') + with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as cCard: + cLabel = ui.label(str(compressor)).style('color: black;') + ui.label('System (Sys)') + with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as sysCard: + sysLabel = ui.label(str(sysSen)).style('color: black;') + with ui.column(): + ui.label('High Pressure Tank (HPT)') + with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as hptCard: + hptLabel = ui.label(str(hpt)).style('color: black;') + ui.label('Blow Out (BO)') + with ui.card().style(f'background-color: grey; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') as boCard: + boLabel = ui.label(str(boSen)).style('color: black;') + + + while True: # read coils from OpenPLC @@ -98,8 +261,10 @@ def thread_nicegui(): logging.error("Main : Read from OpenPLC failed - " + str(e)) # get roughly one second - if delay > 1000: + if delay > 50: delay = 0 + timer = timer + 1 + DISPLAYoverlay2.set_text(str(timer)) if gstSig > 0: gst = gst+random.randint(0, 5) if compressor > 0: @@ -107,6 +272,8 @@ def thread_nicegui(): hpt = hpt+1 if systemValve > 0: hpt = hpt-random.randint(0, 1) + if boSen > 0: + hpt=hpt-random.randint(0, 5) delay = delay+1 # System Valve @@ -122,22 +289,85 @@ def thread_nicegui(): sysSen=1 sysCard.style(f'background-color: green; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') sysLabel.set_text("Operational: " + str(sysSen)) + SoverlayWorking.style( + 'position: absolute; top: 140px; left: 680px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + SoverlayError.style( + 'position: absolute; top: 165px; left: 680px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) else: sysSen=0 sysCard.style(f'background-color: red; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') sysLabel.set_text("Non operational: " + str(sysSen)) + SoverlayWorking.style( + 'position: absolute; top: 140px; left: 680px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) + SoverlayError.style( + 'position: absolute; top: 165px; left: 680px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + + # Control System Valve Overlay + if systemValve>0: + SVoverlayOpen.style( + 'position: absolute; top: 245px; left: 620px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + SVoverlayClosed.style( + 'position: absolute; top: 270px; left: 620px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) + else: + SVoverlayOpen.style( + 'position: absolute; top: 245px; left: 620px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) + SVoverlayClosed.style( + 'position: absolute; top: 270px; left: 620px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) # Blowout if the HPTpressure if over 220 until HTP pressure < 201 if hpt>220 or (boSen==1 and hpt>200): boSen=1 boCard.style(f'background-color: red; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') - boLabel.set_text("Open: " + str(boSen)) - hpt=hpt-random.randint(0, 5) + boLabel.set_text("Open: " + str(boSen)) + BOoverlayOpen.style( + 'position: absolute; top: 55px; left: 680px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + BOoverlayClosed.style( + 'position: absolute; top: 80px; left: 680px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) else: boSen=0 boCard.style(f'background-color: green; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') boLabel.set_text("Closed: " + str(boSen)) + BOoverlayOpen.style( + 'position: absolute; top: 55px; left: 680px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) + BOoverlayClosed.style( + 'position: absolute; top: 80px; left: 680px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) if gst>255: gst=255 @@ -176,38 +406,231 @@ def thread_nicegui(): cLabel.set_text(str(compressor)) hptLabel.set_text(str(hpt)) + # HPT status if hpt == 0: hptCard.style(f'background-color: blue; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') hptLabel.set_text("Empty: " + str(hpt)) + HPToverlayEmpty.style( + 'position: absolute; top: 210px; left: 495px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: block;' + ) + HPToverlayLow.style( + 'position: absolute; top: 184px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayNormal.style( + 'position: absolute; top: 158px; left: 495px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayHigh.style( + 'position: absolute; top: 132px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayCritical.style( + 'position: absolute; top: 106px; left: 495px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) elif hpt < 50: hptCard.style(f'background-color: white; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') hptLabel.set_text("Low: " + str(hpt)) + HPToverlayEmpty.style( + 'position: absolute; top: 210px; left: 495px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayLow.style( + 'position: absolute; top: 184px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: block;' + ) + HPToverlayNormal.style( + 'position: absolute; top: 158px; left: 495px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayHigh.style( + 'position: absolute; top: 132px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayCritical.style( + 'position: absolute; top: 106px; left: 495px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) elif hpt < 100: hptCard.style(f'background-color: green; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') hptLabel.set_text("Normal: " + str(hpt)) + HPToverlayEmpty.style( + 'position: absolute; top: 210px; left: 495px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayLow.style( + 'position: absolute; top: 184px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayNormal.style( + 'position: absolute; top: 158px; left: 495px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + HPToverlayHigh.style( + 'position: absolute; top: 132px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayCritical.style( + 'position: absolute; top: 106px; left: 495px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) elif hpt < 150: hptCard.style(f'background-color: orange; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') hptLabel.set_text("High: " + str(hpt)) + HPToverlayEmpty.style( + 'position: absolute; top: 210px; left: 495px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayLow.style( + 'position: absolute; top: 184px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayNormal.style( + 'position: absolute; top: 158px; left: 495px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayHigh.style( + 'position: absolute; top: 132px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: block;' + ) + HPToverlayCritical.style( + 'position: absolute; top: 106px; left: 495px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) else: hptCard.style(f'background-color: red; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') hptLabel.set_text("Critical: " + str(hpt)) + HPToverlayEmpty.style( + 'position: absolute; top: 210px; left: 495px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayLow.style( + 'position: absolute; top: 184px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayNormal.style( + 'position: absolute; top: 158px; left: 495px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayHigh.style( + 'position: absolute; top: 132px; left: 495px; border-radius: 50%;' + 'background-color: white; width: 5px; height: 5px;' + 'display: none;' + ) + HPToverlayCritical.style( + 'position: absolute; top: 106px; left: 495px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + # Display for compressor if compressor: cCard.style(f'background-color: green; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') cLabel.set_text("ON: " + str(compressor)) + CoverlayOn.style( + 'position: absolute; top: 95px; left: 355px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + CoverlayOff.style( + 'position: absolute; top: 125px; left: 355px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) else: cCard.style(f'background-color: red; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') cLabel.set_text("OFF: " + str(compressor)) + CoverlayOn.style( + 'position: absolute; top: 95px; left: 355px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) + CoverlayOff.style( + 'position: absolute; top: 125px; left: 355px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + # Display style for GST if gst < 50: gstCard.style(f'background-color: red; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') gstLabel.set_text("Low: " + str(gst)) + GSToverlayLow.style( + 'position: absolute; top: 140px; left: 115px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: block;' + ) + GSToverlayNormal.style( + 'position: absolute; top: 110px; left: 115px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) + GSToverlayHigh.style( + 'position: absolute; top: 80px; left: 115px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: none;' + ) elif gst < 150: gstCard.style(f'background-color: green; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') gstLabel.set_text("Normal: " + str(gst)) + GSToverlayLow.style( + 'position: absolute; top: 140px; left: 115px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) + GSToverlayNormal.style( + 'position: absolute; top: 110px; left: 115px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: block;' + ) + GSToverlayHigh.style( + 'position: absolute; top: 80px; left: 115px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: none;' + ) else: gstCard.style(f'background-color: blue; width: 200px; height: 100px; display: flex; justify-content: center; align-items: center;') gstLabel.set_text("Full: " + str(gst)) + GSToverlayLow.style( + 'position: absolute; top: 140px; left: 115px; border-radius: 50%;' + 'background-color: red; width: 5px; height: 5px;' + 'display: none;' + ) + GSToverlayNormal.style( + 'position: absolute; top: 110px; left: 115px; border-radius: 50%;' + 'background-color: green; width: 5px; height: 5px;' + 'display: none;' + ) + GSToverlayHigh.style( + 'position: absolute; top: 80px; left: 115px; border-radius: 50%;' + 'background-color: blue; width: 5px; height: 5px;' + 'display: block;' + ) - time.sleep(0.001) # OpenPLC has a Cycle time of 50ms + time.sleep(0.02) # OpenPLC has a Cycle time of 50ms diff --git a/software/hwio-virtual/CybICS_logo.png b/software/hwio-virtual/pics/CybICS_logo.png similarity index 100% rename from software/hwio-virtual/CybICS_logo.png rename to software/hwio-virtual/pics/CybICS_logo.png diff --git a/software/hwio-virtual/favicon.ico b/software/hwio-virtual/pics/favicon.ico similarity index 100% rename from software/hwio-virtual/favicon.ico rename to software/hwio-virtual/pics/favicon.ico diff --git a/software/hwio-virtual/pics/pcb.png b/software/hwio-virtual/pics/pcb.png new file mode 100644 index 0000000..88a54e4 Binary files /dev/null and b/software/hwio-virtual/pics/pcb.png differ