2021年12月23日 星期四

HFSS API用來載入激發源設定的csv檔

現有的AEDT API不支援載入激發源的csv,這裡我自己寫了一個。

oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oModule = oDesign.GetModule("Solutions")


def loadExcitation(csv_path):
with open(csv_path) as f:
text = f.readlines()

if 'Source,Magnitude,Phase,Terminated,Resistance,Reactance' in text[0]:
items = []
for line in text[1:]:
Source, Magnitude, Phase, Terminated, Resistance, Reactance = line.strip().split(',')
try:
if Terminated == '1':
items.append([
"Name:=" , Source,
"Terminated:=" , True,
"Resistance:=" , Resistance,
"Reactance:=" , Reactance
])
else:
items.append([
"Name:=" , Source,
"Terminated:=" , False,
"Magnitude:=" , Magnitude,
"Phase:=" , Phase
])
except:
pass
AddWarningMessage(str(items))
oModule.EditSources(
[
[
"UseIncidentVoltage:=" , False,
"IncludePortPostProcessing:=", False,
"SpecifySystemPower:=" , False
]
] + items)

else:
items = []
for line in text[1:]:
try:
Source ,Magnitude ,Phase = line.strip().split(',')
items.append([
"Name:=" , Source,
"Magnitude:=" , Magnitude,
"Phase:=" , Phase])
except:
pass
AddWarningMessage(str(items))
oModule.EditSources(
[
[
"IncludePortPostProcessing:=", False,
"SpecifySystemPower:=" , False
]
] + items)


loadExcitation('d:/demo/sss2.csv')


將3D Layout當中沒有封閉的Polyline封閉成Polygon

如果沒有封閉的曲線不是一個polyline,而是多個分離的線段(line),那麼可以先執行Draw > Stitch Lines將其轉換成單一Polyline,再執行下面腳本。形成封閉polygon之後,接下來便可以進行電磁模擬。

oDesktop.ClearMessages("", "", 2)

oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.GetActiveEditor()

selected = oEditor.GetSelections()

for i in selected:
data = []
placement_layer = oEditor.GetPropertyValue('BaseElementTab', i, 'PlacementLayer')

for j in oEditor.GetProperties('BaseElementTab', i):
if j.startswith('Pt'):
location = oEditor.GetPropertyValue('BaseElementTab', i, j)
x, y = [float(k) for k in location.split(',')]
data += ["x:=", x, "y:=", y]

name = oEditor.CreatePolygon(
[
"NAME:Contents",
"polyGeometry:=",
["Name:=", "poly_1002", "LayerName:=", placement_layer, "lw:=", "0", "n:=", len(data), "U:=", "mm", ] + data
])
AddWarningMessage('{} is created!'.format(name))

oEditor.Delete(selected)



2021年12月7日 星期二

在Q3D輸出頻率點

在Q3D當中找不到輸出頻率的API函數。一個方法是在模擬完成之後,用下面程式碼輸出頻率值:

import ScriptEnv
ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oDesktop.ClearMessages("", "", 2)
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()

oModule = oDesign.GetModule("ReportSetup")
arr = oModule.GetSolutionDataPerVariation(
"Matrix",
"Setup1 : Sweep1",
["Context:=", "Original"],
['Freq:=', ['All']],
["mag(Freq)"])

freqs = arr[0].GetRealDataValues("mag(Freq)")
AddWarningMessage(str(freqs))

2021年12月1日 星期三

匯入.asc的程式碼

 PADS .asc轉檔程式已沒有更新,不建議使用

import os

os.environ['path'] = 'C:\Program Files\AnsysEM\AnsysEM21.2\Win64'

os.system('padstoanf.exe d:/demo/41266AAB-JOB.asc')
os.system('anfv4toanfv2.exe d:/demo/PadsLayout.anf')

oTool = oDesktop.GetTool("ImportExport")
oTool.ImportANFV2("D:/demo/PadsLayout_V2.anf", "D:/temp/PadsLayout_V21.aedb", "", "")

2021年11月30日 星期二

AEDT設計屬性匯出及匯入

 exportDesignProperties.py

import clr
clr.AddReference("System.Windows.Forms")

from System.Windows.Forms import DialogResult, SaveFileDialog
dialog = SaveFileDialog()
dialog.Title = "Export Design Properties"
dialog.Filter = "csv file (*.csv)|*.csv"

if dialog.ShowDialog() == DialogResult.OK:
csv_path = dialog.FileName
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()

data = []
for key in oDesign.GetProperties('LocalVariableTab', "LocalVariables"):
value = oDesign.GetPropertyValue('LocalVariableTab', "LocalVariables", key)
data.append((key, value))

with open(csv_path, 'w') as f:
for key, value in data:
f.writelines('{}, {}\n'.format(key, value))
else:
pass

importDesignProperties.py

import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import DialogResult, OpenFileDialog

dialog = OpenFileDialog()
dialog.Multiselect = False
dialog.Title = "HFSS ffd to csv converter"
dialog.Filter = "csv file (*.csv)|*.csv"

if dialog.ShowDialog() == DialogResult.OK:
csv_path = dialog.FileName

with open(csv_path) as f:
text = f.readlines()

data = []
for line in text:
try:
key, value = line.split(',')[0:2]
data.append((key, value))
except:
pass

oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
for key, value in data:
oDesign.ChangeProperty(
[
"NAME:AllTabs",
[
"NAME:LocalVariableTab",
[
"NAME:PropServers",
"LocalVariables"
],
[
"NAME:ChangedProps",
[
"NAME:{}".format(key),
"Value:=" , value
]
]
]
])
else:
pass

2021年11月26日 星期五

輸出變數及使用該變數的物件名稱

輸出變數及使用該變數的物件名稱

import os, re

oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.SetActiveEditor("3D Modeler")

oDesktop.ClearMessages("", "", 2)

properties = oDesign.GetProperties('LocalVariableTab', "LocalVariables")
prj_path = os.path.join(oProject.GetPath(), oProject.GetName() + '.aedt')
design_name = oDesign.GetName()

with open(prj_path) as f:
text = f.readlines()

models = []
for n, line in enumerate(text):
if "$begin 'HFSSModel'" in line:
model = []
elif "$end 'HFSSModel'" in line:
models.append(model)
del (model)

try:
model.append(line)
except:
pass

data = {}
for model in models:
if design_name not in model[2]:
continue
for prop_name in properties:
for line in model:
m = re.search("Name=(.*)", line)
if m:
obj = m.group(1).replace("'", '')
continue

if prop_name in line and '=' in line and 'obj' in locals():
try:
if obj not in data[prop_name]:
data[prop_name] += [obj]
except:
data[prop_name] = [obj]

for i, v in data.items():
AddWarningMessage(str(i) + ': ' + str(v))


2021年11月20日 星期六

輸出License使用狀況

 可以讀取ANSYS License Server的logs_backup目錄並輸出一段期間License的使用狀況

import os
import codecs
import logging
from datetime import datetime, timedelta

FORMAT = '%(asctime)s %(levelname)s: %(message)s'
logging.basicConfig(filename='simulation.log', filemode='w')

path = r'D:\demo\logs_backup'
data = []
pair = {}
Nin, Nout = 0, 0
for file in os.listdir(path):
with codecs.open(os.path.join(path, file), 'r', 'utf-8', errors='ignore') as f:
text = f.readlines()

for line in text:
if len(line.strip()) == 0:
continue

if '(lmgrd) TIMESTAMP' in line:
date = datetime.strptime(line.strip().split()[-1], '%m/%d/%Y')

if 'date' not in locals():
continue

x = line.strip().split()[0]

if not x.count(':') == 2:
continue

time = datetime.strptime(x, '%H:%M:%S').time()
ts = datetime.combine(date, time)

if 't0' not in locals():
t0 = ts
continue
else:
if (ts - t0).total_seconds() < 0:
date += timedelta(days=1)
t0 = datetime.combine(date, time)

try:
time_stamp, _, status, increment, userandserver, pid = line.strip().split()[0:6]
if status not in ['OUT:', 'IN:']:
raise Exception
except:
continue

user, server = userandserver.split('@')
try:
if status == 'OUT:':
pair[(user, server, increment, pid)] = t0
elif status == 'IN:':
tout = pair[(user, server, increment, pid)]
tin = t0
data.append((user, increment, server, str(tout), str(tin), str(tin - tout)))
except:
logging.exception('')

with open('d:/demo/usage.csv', 'w') as f:
width = '{:12}\t{:30}\t{:30}\t{:25}\t{:25}\t{:25}\n'
title = ['USER', 'License', 'Machine', 'Tstart', 'Tstop', 'Duration']
f.writelines(width.format(*title))

for item in data:
f.writelines(width.format(*item))


2021年11月17日 星期三

使用ElementTree處理stack.xml

以下包含xml建立,讀取,改寫,存檔等動作:


import xml.etree.ElementTree as ET
tree = ET.parse('d:/demo/stackup1.xml')
x = tree.getroot()


y = x.find('./Stackup/ELayers/Layers')
for child in y:
print(child.get('Elevation'))
child.set('UnionPrimitives', 'true')
child.set('Material', 'Copper')
tree.write('d:/demo/stachkup2.xml')

2021年11月12日 星期五

計算選取物件的XYZ邊界

 選擇多個物件,執行下面script,即可輸出選取物件的XYZ邊界座標。

import ScriptEnv

ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.SetActiveEditor("3D Modeler")


def setModel(x):
objs = {True: [i for i in x if x[i]], False: [i for i in x if not x[i]]}
for b, o in objs.items():
if o:
oEditor.ChangeProperty(
[
"NAME:AllTabs",
[
"NAME:Geometry3DAttributeTab",
[
"NAME:PropServers",
] + o,
[
"NAME:ChangedProps",
[
"NAME:Model",
"Value:=" , b
]
]
]
])

def getBoundingBox(objs):
db = {}
for i in range(oEditor.GetNumObjects()):
name = oEditor.GetObjectName(i)
p = oEditor.GetPropertyValue('Geometry3DAttributeTab', name, 'Model')
db[name] = p

setModel({i :(i in oEditor.GetSelections()) for i in db.keys()})
result = oEditor.GetModelBoundingBox()
setModel(db)
return result

size = getBoundingBox(oEditor.GetSelections())
AddWarningMessage(str(size))




2021年11月10日 星期三

HFSS 3D Layout當中找出互連的物件群組

分層找出物理上互連的物件群組,針對沒有netname的物件也可以使用。這裡用到遞迴的函數。

import json
import ScriptEnv
ScriptEnv.Initialize("Ansoft.ElectronicsDesktop")
oDesktop.RestoreWindow()
oProject = oDesktop.GetActiveProject()
oDesign = oProject.GetActiveDesign()
oEditor = oDesign.GetActiveEditor()

objdic = {}
for layer in oEditor.GetStackupLayerNames():
objdic[layer] = oEditor.FilterObjectList('Type', '*', oEditor.FindObjects('Layer', layer))

result = {}
for layer in objdic:
for obj in objdic[layer]:
poly = oEditor.GetPolygon(obj)
x = oEditor.FindObjectsByPolygon(poly, layer)
if len(x) > 1:
result[obj] = x

def getConnect(key, g):
for i in result[key]:
if i not in g:
g.append(i)
getConnect(i, g)
else:
continue
return g

group = []
for obj in result:
x = getConnect(obj, [])
if sorted(x) not in group:
group.append(sorted(x))

with open('d:/demo/connect.json', 'w') as f:
json.dump(group, f, indent=4)
(圖一)輸出互連狀況


2021年11月9日 星期二

如何背景執行Python檔

如果我們想執行一隻Python程式,不會跳出任何視窗,只在背景模式執行,比方說監控電腦狀態並輸出log檔,可以使用pythonw.exe搭配start,就不會跳出任何視窗,範例如下。

test.bat

path=D:\myvenv\Scripts
start pythonw .\test.py

test.py

import os
import time
os.chdir(os.path.dirname(__file__))

while True:
with open('test.log', 'a') as f:
f.writelines(str(time.time()) + '\n')
time.sleep(1)

進一步我們可以將該程式(.bat)設定為開機執行。選擇該程式並按Windows鍵+R,跳出下面視窗再按確定即可。



2021年10月29日 星期五

利用desktopproxy中斷.aedt模擬

我們可以用指令執行aedt模擬,比如說

set path=C:\Program Files\AnsysEM\v221\Win64;%path%

start ansysedt.exe -ng -BatchSolve .\package.aedt

上述案例執行時間4分17秒。 

接下來我們用ansysedt指令執行,2分鐘後以desktopproxy -abort中斷模擬。

set path=C:\Program Files\AnsysEM\v221\Win64;%path%

start ansysedt.exe -ng -BatchSolve .\package.aedt

PING localhost -n 120 >NUL

desktopproxy -abort .\package.aedt

中斷之後,再重啟模擬到模擬結束共2分22秒

set path=C:\Program Files\AnsysEM\v221\Win64;%path%

start ansysedt.exe -ng -BatchSolve .\package.aedt

總共花了4分22秒,比一次完成執行僅僅多了約5秒。代表中斷之後重啟模擬是延續之前中斷之前的狀態,而非重新開始。

Steamlit手動排序項目


import time
import streamlit as st


def submit_up(n):
if n > 0:
x = st.session_state.waiting_list[n]
y = st.session_state.waiting_list[n - 1]
st.session_state.waiting_list[n - 1] = x
st.session_state.waiting_list[n] = y


def submit_down(n):
if n < len(st.session_state.waiting_list) - 1:
x = st.session_state.waiting_list[n]
y = st.session_state.waiting_list[n + 1]
st.session_state.waiting_list[n + 1] = x
st.session_state.waiting_list[n] = y


def submit_top(n):
st.session_state.waiting_list = [st.session_state.waiting_list.pop(n)] + st.session_state.waiting_list


def submit_bottom(n):
st.session_state.waiting_list = st.session_state.waiting_list + [st.session_state.waiting_list.pop(n)]


def delete(n):
st.session_state.waiting_list.pop(n)


if 'waiting_list' not in st.session_state:
st.session_state.waiting_list = ['A1', 'B2', 'C3', 'D4']

for n, i in enumerate(st.session_state.waiting_list):
with st.expander(i):
c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 = st.columns(10)
with c1:
st.button('▲', key=f'move up_{i}', on_click=submit_up, args=(n,))
with c2:
st.button('▼', key=f'move down_{i}', on_click=submit_down, args=(n,))
with c3:
st.button('Top', key=f'move up_{i}', on_click=submit_top, args=(n,))
with c4:
st.button('Bottom', key=f'move down_{i}', on_click=submit_bottom, args=(n,))
with c8:
st.button('Delete', key=f'delete_{i}', on_click=delete, args=(n,))
(圖一)手動排序選單


如何用程式碼更改AEDT選項設定

AEDT當中所有的設定都存放在XML檔案當中:C:\Users\<UserName>\Documents\Ansoft\<AnsysProductNameversion>\config\<PC_NAME>_user.XML,比方HPC的設定等等。可以透過指令修改,比方:oDesktop.SetRegistryString('Desktop\Settings\ProjectOptions\HPCLicenseType' , 'Pool')。

處理機碼的指令包含如下:

  • DeleteRegistryEntry
  • DoesRegistryValueExist
  • GetRegistryInt
  • GetRegistryString
  • SetRegistryFromFile
  • SetRegistryInt
  • SetRegistryString

其他像是AEDT Option當中的選項設定也可以透過上述的函式來讀取或修改。

(圖一)AEDT選項設定視窗


2021年10月25日 星期一

不同資料結構對應之表格


import pandas as pd
import streamlit as st
from streamlit_autorefresh import st_autorefresh
import json
count = st_autorefresh(interval=1000)

x = {'name':['a','b','c'], 'Year':[4,5,6]}
st.table(pd.DataFrame(x))

y = [{'name':'a', 'Year':4}, {'name':'b', 'Year':5}, {'name':'c', 'Year':6}]
st.table(pd.DataFrame(y))

z = {'John':{'name':'a', 'Year':'4'}, 'Mark':{'name':'b', 'Year':'5'}, 'Eddy':{'name':'c', 'Year':'6'}}
st.table(pd.DataFrame(z))

w = {'John':{'name':'a', 'Year':4}, 'Mark':{'name':'b', 'Year':5}, 'Eddy':{'name':'c', 'Year':6}}
st.table(pd.DataFrame(w).T)
st.dataframe(pd.DataFrame(w).T)




2021年10月20日 星期三

透過頁面監視檔案夾

 

(圖一)頁面監看檔案夾

import streamlit as st
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from streamlit_autorefresh import st_autorefresh

count = st_autorefresh(interval=1000)

class MyHandler(FileSystemEventHandler):
def on_created(self, event):
with open('state.log', 'a') as f:
f.writelines("on_created: {}\n".format(event.src_path))

def on_deleted(self, event):
with open('state.log', 'a') as f:
f.writelines("on_deleted: {}\n".format(event.src_path))

if 'event_handler' not in st.session_state:
st.session_state.event_handler = MyHandler()
st.session_state.observer = Observer()
st.session_state.observer.schedule(st.session_state.event_handler, path='d:/demo', recursive=True)
st.session_state.observer.start()

try:
with open('state.log') as f:
text = f.readlines()
st.text(''.join(text))
except:
pass

如何在SIwave匯出Pin Group的設定,並在另一個設計匯入

在SIwave匯出Pin Group的設定,並在另一個設計匯入。

(圖一) SIwave Pin Group設定

exportPG_v2.py

import os
import json
import logging

os.chdir(os.path.dirname(__file__))
logging.basicConfig(filename='simulation.log', encoding='utf-8', filemode='w', level=logging.DEBUG)

oDoc = oApp.GetActiveProject()
oDoc.ScrExportComponentFile('temp.cmp')
with open('temp.cmp') as f:
text = f.readlines()

pg = {}
for line in text:
if line.startswith('B_IC') or line.startswith('B_IO'):
_, refdes, part = line.split()

if line.startswith('B_PIN_GROUP '):
group_name = line.split()[1]
key = ','.join([part[1:-1], refdes[1:-1], group_name])
continue

if 'E_PIN_GROUP' in line:
if 'key' in locals():
del (key)
continue

if 'key' in locals():
pg[key] = line.split()

logging.info(pg)

with open('pg_info.json', 'w') as f:
json.dump(pg, f, indent=4)

importPG_v2.py

import os
import json
import logging

os.chdir(os.path.dirname(__file__))
logging.basicConfig(filename='simulation.log', encoding='utf-8', filemode='w', level=logging.DEBUG)

oDoc = oApp.GetActiveProject()
with open('pg_info.json') as f:
data = json.load(f)

logging.info(data)
for key, pins in data.items():
part_name, refdes_name, group_name = key.split(',')
oDoc.ScrCreatePinGroups(part_name, refdes_name, pins, group_name, False)

2021年10月16日 星期六

License Increment簡化

# -*- coding: utf-8 -*-
import streamlit as st

st.header("License Increment輸出工具")
st.markdown("**從自己的license檔案取得與客戶license相同的Increments建立新的license。注意:數量無法改變!**")
large_license = st.file_uploader("上傳自己的ansyslmd.lic")
small_license = st.file_uploader("上傳客戶的ansyslmd.lic")

if large_license and small_license:
header = []
license_items = {}

text = large_license.getvalue().decode("utf-8").splitlines()

for line in text:
if line.startswith('INCREMENT'):
feature = line.split()[1]
license_items[feature] = [line]
else:
try:
license_items[feature].append(line)
except:
header.append(line)

text = small_license.getvalue().decode("utf-8").splitlines()

extracted_features = []
for line in text:
if line.startswith('INCREMENT'):
feature = line.split()[1]
extracted_features.append(feature)

text = '\n'.join(header) + '\n'
for feature in extracted_features:
text += '\n'.join(license_items[feature]) + '\n'
st.subheader('{} Increments 輸出:'.format(len(extracted_features)))
st.write(',\n'.join(extracted_features))

st.download_button('下載檔案', file_name='new_ansyslmd.lic', data=text)

2021年10月9日 星期六

Python虛擬環境安裝及啟動

Python安裝完畢之後,我們只有一個非常基本的環境。除了Python命令視窗,沒有Spyder編輯器,也沒有numpy, scipy, matplotlib, pyqt等模組。這時候我們可以建立一組全新的虛擬環境。虛擬環境是最單純的環境,當中只有最小的Python核心。接著便可以在虛擬環境安裝之後開發需要用的套件模組。

首先到Python目錄底下,執行下面指令在env03建立虛擬環境。底下顯示的是在筆者安裝Python3.8的目錄底下建立虛擬環境。

C:\Users\mlin\AppData\Local\Programs\Python\Python38> .\python -m venv D:\demo_env\env03

接著切換目錄到虛擬環境的Script目錄當中,執行activate,便可以進入虛擬環境的console,此時輸入行前端會出現(env03)。

(圖二) activate進入虛擬環境

接著便可以繼續執行pip install matplotlib, pip install spyder, pip install pyqt5, pip install pyqt5-tools等等指令來安裝必要套件。安裝套件需耗時數分鐘至數十分鐘不等。

(圖三)pip install安裝套件

完成之後便可以在虛擬環境當中執行python, spyder, jupyter notebook等指令來使用工具。也可以為這些工具建立捷徑方便之後使用。虛擬環境不需要時,可直接刪除該目錄即可。

2021年10月7日 星期四

用ansysedt.exe讀取網表做電路模擬

之前介紹過用nexxim.exe讀取網表作電路模擬,輸出.sdf並將.sdf轉成.csv。缺點是只能支援線性模擬及暫態模擬。且線性模擬只能輸出S參數,不能輸出Y/Z參數。除此之外,必須自行編寫程式來輸出Touchstone格式,非常不方便。本篇做法可以處理AEDT的各類電路模擬,並且以.csv輸出任意AEDT本身支援的模擬資料,如頻譜,眼圖等等。

1. 建立AEDT專案test.aedt,加入一個設計CircuitNetlist1,當中只有一行: .INCLUDE "D:\demo\test.cir",如下圖。並在專案當中加入"Z Parameter Table 1"的表格報告。完成之後關閉aedt。

(圖一)test.aedt專案包含CircuitNetlist1設計

2. 文字檔test.cir當中包含完整的網表及模擬設定,例如:

C1 Port1 Port2 1e-3
RPort1 Port1 0 PORTNUM=1 RZ=50 IZ=0
.PORT Port1 0 1 RPort1
RPort2 Port2 0 PORTNUM=2 RZ=50 IZ=0
.PORT Port2 0 2 RPort2

* end toplevel circuit
.LNA
+ LIN 101 0 1000000000
+ FLAG='LNA'

3.建立.py檔,執行模擬及.csv輸出,如下

oProject = oDesktop.OpenProject("d:/demo/test.aedt")
oDesign = oProject.SetActiveDesign("CircuitNetlist1")
oDesign.AnalyzeAll()
oModule = oDesign.GetModule("ReportSetup")

oModule.ExportToFile("Z Parameter Table 1", "D:/demo/Z Parameter Table 1.csv", False)

4.執行ansysedt命令輸出.csv檔

SET PATH=C:\Program Files\AnsysEM\AnsysEM21.1\Win64
ansysedt.exe -features=beta -ng -waitforlicense -RunScriptAndExit .\test.py

自動化程式可以透過修改test.cir當中的網表內容並執行ansysedt命令來取得模擬結果.csv檔來製作報告。

2021年10月1日 星期五

如何在HTML嵌入圖片?

我們可以將圖片的Base64碼直接寫在html當中,就可以不用另外傳送圖片檔了。

<img src="data:image/png;base64,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">

(圖一)網頁顯示


 

2021年9月27日 星期一

一次啟動多個Stramlit App

main.py為主頁面,透過main.py可以連接到不同App。

run.bat

set PATH=C:\Users\mlin\AppData\Local\Programs\Python\Python38\Scripts

start streamlit run .\main.py --server.port 8050
start streamlit run .\case1.py --server.port 8051
start streamlit run .\case2.py --server.port 8052
start streamlit run .\case3.py --server.port 8053

main.py

import os
import streamlit as st
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()

st.title('main')
st.markdown(f'[case1](http://{ip}:8051)')
st.markdown(f'[case2](http://{ip}:8052)')
st.markdown(f'[case3](http://{ip}:8053)')

2021年9月21日 星期二

如何用Streamlit做出具備動畫效果的網頁

透過st.empty(),只要以下幾行程式碼就可以做出數學運算的動畫網頁。有趣的是,這個計算是在伺服器完成運算並主動刷新使用者頁面,類似推播的功能。而非由使用者端刷新頁面。

import streamlit as st
import numpy as np
import matplotlib.pyplot as plt

plt.figure(figsize=(6,6))
st.title('Animation')
t = 0
x, y = [], []
n = st.number_input('w:', value=5)
start = st.button('Start!')
container = st.empty()

if start:
while(True):
plt.clf()
plt.title(f't={round(t,2)}')

x.append(np.sin(t))
y.append(np.cos(n*t))

plt.xlim(-1.2,1.2)
plt.ylim(-1.2,1.2)
plt.plot(x, y, color='red')
container.pyplot(plt)
t+=0.02




2021年9月20日 星期一

建立有滑桿,多欄位,Latex方程式的互動式網頁

只要25行,就可以輕鬆建立一個精美的拍頻訊號互動式頁面,布置在任一台電腦上面,就可以從遠端登入網頁使用。

import streamlit as st
import numpy as np
import matplotlib.pyplot as plt

st.title('Beat Signal Demo')

c1, c2 = st.columns(2)
with c1:
a1 = st.slider('A1', 0.0, 1.0, 0.5)
f1 = st.slider('f1(Hz)', 0, 100, 10)

with c2:
a2 = st.slider('A2', 0.0, 1.0, 0.5)
f2 = st.slider('f2(Hz)', 0, 100, 20)

time = np.arange(0, 1, 0.001)
x1 = a1 * np.sin(2*np.pi*f1*time)
x2 = a2 * np.sin(2*np.pi*f2*time)

st.latex('f(t) = A1\cdot sin(2\pi f_1 t) + A2\cdot sin(2\pi f_2 t)')
plt.plot(time, x1+x2)
plt.ylabel('f(t)',)
plt.xlabel('t(sec)',)

st.pyplot(plt)


(圖一)互動式網頁介面


2021年9月18日 星期六

上傳檔案到遠端streamlit網頁伺服器

如果要上傳snp, brd, aedtz, gds, zip, png等檔案到遠端伺服器用自動化程式加以處理,以下程式碼可以產生檔案上傳按鈕,選擇檔案即可上傳到伺服器特定路徑存檔。另外也可以選擇副檔名篩選出檔案類型,並選擇檔案下載。

import streamlit as st
from os import listdir
from os.path import isfile, join

folder_path = 'D:/Downloads'

st.header('上傳檔案')
with st.form(key='my_form', clear_on_submit=True):
files = st.file_uploader("上傳檔案", accept_multiple_files=True, )

if st.form_submit_button(label='Submit'):
for file in files:
with open(join(folder_path, file.name), 'wb') as f:
f.write(file.getbuffer())

st.header('下載檔案')
password = st.text_input('Password', type="password")
if password == '1234':
ext = []
files = listdir(folder_path)
for f in files:
if isfile(join(folder_path, f)):
ext.append(f.split('.')[-1])

ext = list(set(ext))
x = st.selectbox("extensions", ext, 0)
filename = st.selectbox('file', [i for i in files if i.endswith(x)])

with open(join(folder_path, filename), 'rb') as f:
data = f.read()
st.download_button('download', data, filename)
(圖一)上傳/下載檔案頁面


以Streamlit建立網頁GUI背景呼叫Nexxim.exe執行RC電路模擬

延續上一篇介紹的內容,本範例套用Streamlit模組,不到10行程式碼(st.XXX)便可以建立RC電路模擬GUI。使用者輸入R, C,按下執行模擬鍵,一秒便可以顯示S21分布圖。也可以從遠端設備登入網頁執行模擬工作。如圖二。

# -*- coding: utf-8 -*-
import os
import streamlit as st
import random
import matplotlib.pyplot as plt
from datetime import datetime



text = '''
.option PARHIER='local'
.option max_messages=1

* begin toplevel circuit

R1 Port1 Port2 {}
C2 0 Port2 {}
RPort1 Port1 0 PORTNUM=1 RZ=50 IZ=0
.PORT Port1 0 1 RPort1
RPort2 Port2 0 PORTNUM=2 RZ=50 IZ=0
.PORT Port2 0 2 RPort2

* end toplevel circuit
.LNA
+ LIN 10000 0 10000000000
+ FLAG='LNA'

.end'''


def readCSV(csv_name):
with open(csv_name) as f:
text = f.readlines()

freq = []
S11, S12, S21, S22 = [], [], [], []
for line in text[1:]:
_, f, s11_re, s11_im, s12_re, s12_im, s21_re, s21_im, s22_re, s22_im = line.split(',')
freq.append(float(f))
S11.append(complex(float(s11_re), float(s11_im)))
S12.append(complex(float(s12_re), float(s12_im)))
S21.append(complex(float(s21_re), float(s21_im)))
S22.append(complex(float(s22_re), float(s22_im)))
return freq, S21


st.header('RC電路S參數模擬')
R = st.number_input('電阻值(Ohm)')
C = st.number_input('電容值(pF)') * 1e-12
z = st.button('執行模擬')

n = datetime.now().timestamp()
if z:
with open('c:/demo/rc{}.cir'.format(n), 'w') as f:
f.write(text.format(R, C))

os.environ['path'] = 'C:\Program Files\AnsysEM\AnsysEM21.1\Win64'
os.system('nexxim c:/demo/rc{}.cir'.format(n))
os.system('sdf2csv c:/demo/rc{}.cir.sdf'.format(n))
with open('c:/demo/rc{}.cir.sdf.csv'.format(n)) as f:
text = f.readlines()

f, S21 = readCSV('c:/demo/rc{}.cir.sdf.csv'.format(n))
plt.figure(figsize=(4, 3))
plt.plot(f, [abs(i) for i in S21])
plt.grid()
plt.xlabel('freq(Hz)')
plt.ylabel('mag')
plt.xlim(0, 1e10)
plt.ylim(0, 1)
st.pyplot(plt)
print('Finished ID: {}'.format(n))
(圖一)網頁GUI

(圖二)由手機登入並執行模擬