diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..7cf15be8e --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +frontend/ \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 000000000..59d29bd5f --- /dev/null +++ b/.env @@ -0,0 +1 @@ +VITE_BASE_URL=http://127.0.0.1:8000/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index a56b654c9..41c78ef82 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,8 @@ **/.idea/ *~ .vscode -*.sqlite -**/*.sqlite +#*.sqlite +#**/*.sqlite *.log *.bak *.pdf @@ -35,3 +35,48 @@ ResourceFiles/Osi_Files/* *.xlsx ResourceFiles/last_designs/* ResourceFiles/html_page/* +#osdag_web/secret_key.py +db.sqlite3 +frontend/node_modules +osdag/migrations +node_modules +frontend/public/output-obj.obj +file_storage/design_report/*.pdf +file_storage/design_report/*.log +file_storage/design_report/*.tex +file_storage/design_report/*.aux + +# ignore the brep fles +file_storage/cad_models/*.brep + +# ignore companylogo images +file_storage/company_logo/*.png + +# ignore the input values files +file_storage/input_values_files/*.osi +conda_packages + +# ignore the brep files +file_storage/cad_models/*.brep +file_storage/cad_models/*.obj +file_storage/cad_models/*.iges +file_storage/cad_models/*.step +file_storage/cad_models/*.stp +file_storage/cad_models/*.glb +file_storage/osdag_web/firebase-service-account.json +key + +backend/file_storage/cad_models/*.brep +backend/file_storage/cad_models/*.obj +backend/file_storage/cad_models/*.iges +backend/file_storage/cad_models/*.step +backend/file_storage/cad_models/*.stp +backend/file_storage/cad_models/*.glb +backend/file_storage/cad_models/*.parts.json +backend/file_storage/cad_models/*.stl +backend/file_storage/cad_models/*.stp +backend/file_storage/cad_models/*.glb +backend/file_storage/cad_models/*.parts.json +backend/file_storage/cad_models/*.aux + +backend/firebase-service-account.json \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bfbaf5971..000000000 --- a/.travis.yml +++ /dev/null @@ -1,46 +0,0 @@ -{ - "language": "generic", - "os": [ - "linux" - ], - "dist": "bionic", - "env": { - "jobs": [ - { - "CONDA_PYTHON": "3.7" - } - ] - }, - "cache": { - "directories": [ - "$HOME/miniconda", - "/tmp/texlive", - "$HOME/.texlive" - ] - }, - "before_cache": [ - "rm -rf $HOME/miniconda/locks $HOME/miniconda/pkgs $HOME/miniconda/var $HOME/miniconda/envs/test-environment/conda-meta/history $HOME/miniconda/lib/python3.7/__pycache__" - ], - "before_install": [ - "MINICONDA_PATH=$HOME/miniconda;", - "MINICONDA_SUB_PATH=$MINICONDA_PATH/bin;", - "if [[ -f $HOME/download/miniconda.sh ]]; then echo \"miniconda.sh for posix already available from cache\"; else mkdir -p $HOME/download; if [[ \"$TRAVIS_OS_NAME\" == \"linux\" ]]; then echo \"downloading miniconda.sh for linux\"; wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O $HOME/download/miniconda.sh; elif [[ \"$TRAVIS_OS_NAME\" == \"osx\" ]]; then echo \"downloading miniconda.sh for osx\"; wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O $HOME/download/miniconda.sh; fi; fi;" - ], - "install": [ - "if [[ -d $MINICONDA_SUB_PATH ]]; then echo \"miniconda for posix already available from cache\"; else echo \"installing miniconda for posix\"; bash $HOME/download/miniconda.sh -b -u -p $MINICONDA_PATH; fi;", - "export PATH=\"$MINICONDA_PATH:$MINICONDA_SUB_PATH:$PATH\";", - "hash -r;", - "echo $TRAVIS_OS_NAME", - "echo $TRAVIS_PYTHON_VERSION", - "echo $CONDA_PYTHON", - "conda config --set always_yes yes --set changeps1 no;", - "source $MINICONDA_PATH/etc/profile.d/conda.sh", - "conda info --all;", - "conda update --name base --channel defaults --quiet conda;", - "while read requirement; do conda install --yes $requirement || pip install --user $requirement; done < requirements.txt;", - "source ./texlive/texlive_install.sh;" - ], - "script": [ - "python Module_test.py" - ] -} diff --git a/APP_CRASH/Appcrash/__init__.py b/APP_CRASH/Appcrash/__init__.py deleted file mode 100644 index a80047c43..000000000 --- a/APP_CRASH/Appcrash/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -A qt dialog for reporting an issue on github or via email -""" diff --git a/APP_CRASH/Appcrash/_dialogs/gh_login.py b/APP_CRASH/Appcrash/_dialogs/gh_login.py deleted file mode 100644 index fc81bb523..000000000 --- a/APP_CRASH/Appcrash/_dialogs/gh_login.py +++ /dev/null @@ -1,143 +0,0 @@ -from PyQt5 import QtCore, QtWidgets - -from .._forms import dlg_github_login_ui - -GH_MARK_NORMAL = ':/rc/GitHub-Mark.png' -GH_MARK_LIGHT = ':/rc/GitHub-Mark-Light.png' - - -class DlgGitHubLogin(QtWidgets.QDialog): - HTML = '

' \ - '

Sign in to GitHub

' - - def __init__(self, parent, username, remember, password, remember_token, token): - super(DlgGitHubLogin, self).__init__(parent) - self.ui = dlg_github_login_ui.Ui_Dialog() - self.ui.setupUi(self) - self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) - self.username = username - self.remember = remember - self.password = password - self.remember_token = remember_token - self.token = token - self.is_basic = True - mark = GH_MARK_NORMAL - if self.palette().base().color().lightness() < 128: - mark = GH_MARK_LIGHT - html = self.HTML % mark - self.ui.label_6.hide() - self.ui.label_7.hide() - self.ui.label_8.hide() - self.ui.label_9.hide() - self.ui.hl1.hide() - self.ui.lbl_html.setText(html) - self.ui.bt_sign_in.clicked.connect(self.accept) - self.ui.le_username.textChanged.connect(self.update_btn_state) - self.ui.le_password.textChanged.connect(self.update_btn_state) - if self.is_basic: - if self.username=='' and self.password=='': - self.ui.bt_sign_in.setDisabled(True) - else: - if not self.token: - self.ui.bt_sign_in.setDisabled(True) - - self.ui.le_username.setText(self.username) - self.ui.le_password.setText(self.password) - self.ui.cb_remember.setChecked(self.remember) - #self.ui.label_8.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum,QtWidgets.QSizePolicy.Maximum)) - self.adjustSize() - self.setFixedSize(self.width(), self.height()) - self.ui.le_password.installEventFilter(self) - self.ui.le_username.installEventFilter(self) - self.ui.label_4.installEventFilter(self) - - def eventFilter(self, obj, event): - interesting_objects = [self.ui.le_password, self.ui.le_username] - if obj in interesting_objects and event.type() == QtCore.QEvent.KeyPress: - if event.key() == QtCore.Qt.Key_Return and event.modifiers() & QtCore.Qt.ControlModifier and \ - self.ui.bt_sign_in.isEnabled(): - self.accept() - return True - if obj == self.ui.label_4: - if event.type()==QtCore.QEvent.Enter: - self.ui.label_4.setStyleSheet("color:Green;font-size:10pt;font-family:consolas;") - self.adjustSize() - elif event.type()==QtCore.QEvent.Leave: - self.ui.label_4.setStyleSheet("font-size:9pt;font-family:consolas;") - self.adjustSize() - elif event.type()==QtCore.QEvent.MouseButtonPress: - if event.button()==QtCore.Qt.LeftButton: - text = self.ui.label_4.text() - if text == 'Sign in using Personal Access Token': - self.remove_widget() - else: - self.add_Widget() - self.adjustSize() - return True - return False - - def remove_widget(self): - #self.ui.verticalLayout.removeWidget(self.ui.le_password) - #self.ui.le_password.deleteLater() - #self.ui.le_password = None - - #self.ui.verticalLayout.removeWidget(self.ui.label_3) - #self.ui.label_3.deleteLater() - #self.ui.label_3 = None - self.ui.label_6.show() - self.ui.label_7.show() - self.ui.label_8.show() - self.ui.label_9.show() - self.ui.hl1.show() - self.is_basic = False - self.ui.le_username.setText(self.token) - if self.ui.le_username.text(): - self.ui.bt_sign_in.setEnabled(1) - self.ui.le_password.hide() - self.ui.label_3.hide() - self.ui.label_2.setText("Token:") - self.ui.label_4.setText("Basic Authentication") - self.ui.le_username.textChanged.connect(self.change_btn_state) - self.ui.cb_remember.setChecked(self.remember_token) - self.adjustSize() - - def add_Widget(self): - self.ui.label_6.hide() - self.ui.label_7.hide() - self.ui.label_8.hide() - self.ui.label_9.hide() - self.ui.hl1.hide() - self.is_basic = True - if self.username and self.password: - self.ui.bt_sign_in.setEnabled(1) - else: - self.ui.bt_sign_in.setEnabled(0) - self.ui.le_password.setText(self.password) - self.ui.le_username.setText(self.username) - self.ui.le_password.show() - self.ui.label_3.show() - self.ui.label_2.setText("Username:") - self.ui.le_username.textChanged.connect(self.update_btn_state) - self.ui.label_4.setText("Sign in using Personal Access Token") - self.ui.cb_remember.setChecked(self.remember) - self.adjustSize() - - def change_btn_state(self): - if self.ui.le_username.text(): - self.ui.bt_sign_in.setEnabled(1) - else: - self.ui.bt_sign_in.setEnabled(0) - - def update_btn_state(self): - if self.ui.le_username.text()!='' and self.ui.le_password.text()!='': - self.ui.bt_sign_in.setEnabled(1) - else: - self.ui.bt_sign_in.setEnabled(0) - - @classmethod - def login(cls, parent, username, remember, password, remember_token, token): # pragma: no cover - dlg = DlgGitHubLogin(parent, username, remember, password, remember_token, token) - if dlg.exec_() == dlg.Accepted: - return dlg.ui.le_username.text(), dlg.ui.le_password.text(), \ - dlg.ui.cb_remember.isChecked(),dlg.is_basic, dlg.ui.cb_remember.isChecked(), dlg.ui.le_username.text() - return None, None, None, None, None, None diff --git a/APP_CRASH/Appcrash/_dialogs/report.py b/APP_CRASH/Appcrash/_dialogs/report.py deleted file mode 100644 index d72ca047b..000000000 --- a/APP_CRASH/Appcrash/_dialogs/report.py +++ /dev/null @@ -1,87 +0,0 @@ -import logging - -from .._forms import dlg_report_bug_ui -from PyQt5 import QtCore, QtGui, QtWidgets -from .review import DlgReview - - -_logger = logging.getLogger(__name__) - - -LOG_LIMIT = 100 # keep only the last 100 lines - - -class DlgReport(QtWidgets.QDialog): - - - def __init__(self, backends, window_title='Report an issue...', - window_icon=None, traceback=None, issue_title='', - issue_description='', include_log=True, include_sys_info=True, - **kwargs): - super(DlgReport, self).__init__(**kwargs) - self._traceback = traceback - self.window_icon = window_icon - self.ui = dlg_report_bug_ui.Ui_Dialog() - self.ui.setupUi(self) - self.ui.cb_include_sys_info.setChecked(include_sys_info) - self.ui.cb_include_application_log.setChecked(include_log) - self.setWindowTitle(window_title) - self.setWindowIcon(QtGui.QIcon.fromTheme('tools-report-bug') - if window_icon is None else window_icon) - self.ui.lineEditTitle.setText(issue_title) - self.ui.plainTextEditDesc.setPlainText(issue_description) - self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) - - self.ui.lineEditTitle.textChanged.connect(self._enable_buttons) - self.ui.plainTextEditDesc.textChanged.connect(self._enable_buttons) - - self.buttons = [] - for backend in backends: - bt = QtWidgets.QPushButton() - bt.setText(backend.button_text) - if backend.button_icon: - bt.setIcon(backend.button_icon) - bt.backend = backend - bt.clicked.connect(self._on_button_clicked) - self.ui.layout_buttons.addWidget(bt) - self.buttons.append(bt) - self._enable_buttons() - - def _enable_buttons(self, *_): - title = str(self.ui.lineEditTitle.text()).strip() - desc = str(self.ui.plainTextEditDesc.toPlainText()).strip() - enable = title != '' and desc != '' - for bt in self.buttons: - bt.setEnabled(enable) - - def _on_button_clicked(self): - from .. import api - bt = self.sender() - description = self.ui.plainTextEditDesc.toPlainText() - backend = bt.backend - backend.parent_widget = self - title = backend.formatter.format_title( - str(self.ui.lineEditTitle.text())) - - sys_info = None - if self.ui.cb_include_sys_info.isChecked(): - sys_info = api.get_system_information() - - log = None - if self.ui.cb_include_application_log.isChecked(): - log = api.get_application_log() - - body = backend.formatter.format_body( - str(description), sys_info, self._traceback) - - if backend.need_review: # pragma: no cover - body, log = DlgReview.review(body, log, self, self.window_icon) - if body is None and log is None: - return # user cancelled the review dialog - - try: - if backend.send_report(title, body, log) : - self.accept() - except Exception as e: - #QtWidgets.QMessageBox.warning(self, "Failed to send report", "Failed to send report.\n\n%r" % e) - pass diff --git a/APP_CRASH/Appcrash/_dialogs/review.py b/APP_CRASH/Appcrash/_dialogs/review.py deleted file mode 100644 index be6ae7ff8..000000000 --- a/APP_CRASH/Appcrash/_dialogs/review.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -This module contains the review dialog. -""" -from PyQt5 import QtCore, QtGui, QtWidgets -from .._forms import dlg_review_ui - - -class DlgReview(QtWidgets.QDialog): - """ - Dialog for reviewing the final report. - """ - def __init__(self, content, log, parent, window_icon): - """ - :param content: content of the final report, before review - :param parent: parent widget - """ - super(DlgReview, self).__init__(parent) - self.ui = dlg_review_ui.Ui_Dialog() - self.ui.setupUi(self) - self.ui.tabWidget.setCurrentIndex(0) - self.ui.edit_main.setPlainText(content) - self.ui.edit_main.installEventFilter(self) - self.ui.edit_log.installEventFilter(self) - self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint) - self.setWindowIcon( - QtGui.QIcon.fromTheme('document-edit') - if window_icon is None else window_icon) - if log: - self.ui.edit_log.setPlainText(log) - else: - self.ui.tabWidget.tabBar().hide() - self.ui.edit_main.setFocus() - - def eventFilter(self, obj, event): - interesting_objects = [self.ui.edit_log, self.ui.edit_main] - if obj in interesting_objects and event.type() == QtCore.QEvent.KeyPress: - if event.key() == QtCore.Qt.Key_Return and \ - event.modifiers() & QtCore.Qt.ControlModifier: - self.accept() - return True - return False - - @classmethod - def review(cls, content, log, parent, window_icon): # pragma: no cover - """ - Reviews the final bug report. - - :param content: content of the final report, before review - :param parent: parent widget - - :returns: the reviewed report content or None if the review was - canceled. - """ - dlg = DlgReview(content, log, parent, window_icon) - if dlg.exec_(): - return dlg.ui.edit_main.toPlainText(), \ - dlg.ui.edit_log.toPlainText() - return None, None diff --git a/APP_CRASH/Appcrash/_forms/dlg_github_login_ui.py b/APP_CRASH/Appcrash/_forms/dlg_github_login_ui.py deleted file mode 100644 index f67d6264b..000000000 --- a/APP_CRASH/Appcrash/_forms/dlg_github_login_ui.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- - -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - -class Ui_Dialog(object): - def setupUi(self, Dialog): - Dialog.setObjectName("Dialog") - Dialog.resize(366, 248) - Dialog.setMinimumSize(QtCore.QSize(350, 0)) - self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) - self.verticalLayout.setObjectName("verticalLayout") - self.lbl_html = QtWidgets.QLabel(Dialog) - self.lbl_html.setObjectName("lbl_html") - self.verticalLayout.addWidget(self.lbl_html) - self.formLayout = QtWidgets.QFormLayout() - self.formLayout.setContentsMargins(-1, 0, -1, -1) - self.formLayout.setObjectName("formLayout") - self.label_2 = QtWidgets.QLabel(Dialog) - self.label_2.setObjectName("label_2") - self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2) - self.le_username = QtWidgets.QLineEdit(Dialog) - self.le_username.setObjectName("le_username") - self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.le_username) - self.label_3 = QtWidgets.QLabel(Dialog) - self.label_3.setObjectName("label_3") - self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_3) - self.le_password = QtWidgets.QLineEdit(Dialog) - self.le_password.setEchoMode(QtWidgets.QLineEdit.Password) - self.le_password.setObjectName("le_password") - self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.le_password) - self.verticalLayout.addLayout(self.formLayout) - self.cb_remember = QtWidgets.QCheckBox(Dialog) - self.cb_remember.setObjectName("cb_remember") - self.verticalLayout.addWidget(self.cb_remember) - self.bt_sign_in = QtWidgets.QPushButton(Dialog) - self.bt_sign_in.setObjectName("bt_sign_in") - self.verticalLayout.addWidget(self.bt_sign_in) - self.label_5 = QtWidgets.QLabel(Dialog) - self.label_6 = QtWidgets.QLabel(Dialog) - self.verticalLayout.addWidget(self.label_5) - - self.verticalLayout.setSpacing(8) - self.label_4 = QtWidgets.QLabel(Dialog) - self.label_4.setObjectName("label_4") - self.verticalLayout.addWidget(self.label_4) - self.label_4.setAlignment(QtCore.Qt.AlignCenter) - self.label_4.setStyleSheet("font-size:9pt;font-family:consolas;") - - self.verticalLayout.addWidget(self.label_6) - - self.label_7 = QtWidgets.QLabel(Dialog) - self.label_7.setAlignment(QtCore.Qt.AlignCenter) - self.label_7.setText("Don't know how to create a Token? "+'Click here') - self.verticalLayout.addWidget(self.label_7) - self.label_7.setOpenExternalLinks(True) - self.label_7.setStyleSheet("font-size:8pt;font-family:Arial;") - - self.label_9 = QtWidgets.QLabel(Dialog) - self.verticalLayout.addWidget(self.label_9) - self.hl1 = QtWidgets.QFrame(Dialog) - self.hl1.setFrameShape(QtWidgets.QFrame.HLine) - self.verticalLayout.addWidget(self.hl1) - - self.label_8 = QtWidgets.QLabel(Dialog) - self.label_8.setAlignment(QtCore.Qt.AlignCenter) - self.label_8.setStyleSheet("font-size:8pt;font-family:Arial;") - self.label_8.setText("Make sure token has atleast these two permissions:"+"\n \n"+"1. public_repo (To Access public repositories)"+"\n\n"+"2. gist (To Create gists)") - self.verticalLayout.addWidget(self.label_8) - - - self.retranslateUi(Dialog) - QtCore.QMetaObject.connectSlotsByName(Dialog) - - - def retranslateUi(self, Dialog): - _translate = QtCore.QCoreApplication.translate - Dialog.setWindowTitle(_translate("Dialog", "Sign in to github")) - self.lbl_html.setText(_translate("Dialog", "

Sign in to GitHub

")) - self.label_2.setText(_translate("Dialog", "Username:")) - self.label_3.setText(_translate("Dialog", "Password: ")) - self.label_4.setText(_translate("Dialog", "Sign in using Personal Access Token")) - self.label_5.setText(" ") - self.label_6.setText(" ") - self.cb_remember.setText(_translate("Dialog", "Remember me")) - self.bt_sign_in.setText(_translate("Dialog", "Sign in")) - -from . import qcrash_rc diff --git a/APP_CRASH/Appcrash/_forms/dlg_report_bug_ui.py b/APP_CRASH/Appcrash/_forms/dlg_report_bug_ui.py deleted file mode 100644 index 74330f5b2..000000000 --- a/APP_CRASH/Appcrash/_forms/dlg_report_bug_ui.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- - -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - -class Ui_Dialog(object): - def setupUi(self, Dialog): - Dialog.setObjectName("Dialog") - Dialog.resize(544, 272) - self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) - self.verticalLayout.setObjectName("verticalLayout") - self.formLayout = QtWidgets.QFormLayout() - self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow) - self.formLayout.setObjectName("formLayout") - self.label = QtWidgets.QLabel(Dialog) - self.label.setObjectName("label") - self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label) - self.lineEditTitle = QtWidgets.QLineEdit(Dialog) - self.lineEditTitle.setObjectName("lineEditTitle") - self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEditTitle) - self.label_2 = QtWidgets.QLabel(Dialog) - self.label_2.setObjectName("label_2") - self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_2) - self.plainTextEditDesc = QtWidgets.QPlainTextEdit(Dialog) - self.plainTextEditDesc.setTabChangesFocus(True) - self.plainTextEditDesc.setObjectName("plainTextEditDesc") - self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.plainTextEditDesc) - self.cb_include_sys_info = QtWidgets.QCheckBox(Dialog) - self.cb_include_sys_info.setObjectName("cb_include_sys_info") - self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.cb_include_sys_info) - self.cb_include_application_log = QtWidgets.QCheckBox(Dialog) - self.cb_include_application_log.setObjectName("cb_include_application_log") - self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.cb_include_application_log) - self.verticalLayout.addLayout(self.formLayout) - self.layout_buttons = QtWidgets.QHBoxLayout() - self.layout_buttons.setContentsMargins(-1, 0, -1, -1) - self.layout_buttons.setObjectName("layout_buttons") - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.layout_buttons.addItem(spacerItem) - self.verticalLayout.addLayout(self.layout_buttons) - - self.retranslateUi(Dialog) - QtCore.QMetaObject.connectSlotsByName(Dialog) - - def retranslateUi(self, Dialog): - _translate = QtCore.QCoreApplication.translate - Dialog.setWindowTitle(_translate("Dialog", "Report an issue...")) - self.label.setText(_translate("Dialog", "Title:")) - self.lineEditTitle.setToolTip(_translate("Dialog", "Title of the issue")) - self.label_2.setText(_translate("Dialog", "Description:")) - self.plainTextEditDesc.setToolTip(_translate("Dialog", "Description of the issue (mandatory)")) - self.cb_include_sys_info.setToolTip(_translate("Dialog", "Include system information")) - self.cb_include_sys_info.setText(_translate("Dialog", "Include system &information")) - self.cb_include_application_log.setToolTip(_translate("Dialog", "Include application log")) - self.cb_include_application_log.setText(_translate("Dialog", "Include application &log")) - -from . import qcrash_rc diff --git a/APP_CRASH/Appcrash/_forms/dlg_review_ui.py b/APP_CRASH/Appcrash/_forms/dlg_review_ui.py deleted file mode 100644 index 7c195fb74..000000000 --- a/APP_CRASH/Appcrash/_forms/dlg_review_ui.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- - -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - -class Ui_Dialog(object): - def setupUi(self, Dialog): - Dialog.setObjectName("Dialog") - Dialog.resize(622, 495) - icon = QtGui.QIcon.fromTheme("document-edit") - Dialog.setWindowIcon(icon) - Dialog.setToolTip("") - self.verticalLayout = QtWidgets.QVBoxLayout(Dialog) - self.verticalLayout.setObjectName("verticalLayout") - self.label = QtWidgets.QLabel(Dialog) - self.label.setStyleSheet("background-color:palette(highlight) ;\n" -"color: palette(highlighted-text);\n" -"padding: 10px;\n" -"border-radius:3px;") - self.label.setObjectName("label") - self.verticalLayout.addWidget(self.label) - self.tabWidget = QtWidgets.QTabWidget(Dialog) - self.tabWidget.setObjectName("Report_Issue_tabWidget") - self.tab = QtWidgets.QWidget() - self.tab.setObjectName("tab") - self.gridLayout = QtWidgets.QGridLayout(self.tab) - self.gridLayout.setObjectName("gridLayout") - self.edit_main = QtWidgets.QPlainTextEdit(self.tab) - self.edit_main.setTabChangesFocus(True) - self.edit_main.setObjectName("edit_main") - self.gridLayout.addWidget(self.edit_main, 0, 0, 1, 1) - self.tabWidget.addTab(self.tab, "") - self.tab_2 = QtWidgets.QWidget() - self.tab_2.setObjectName("tab_2") - self.gridLayout_2 = QtWidgets.QGridLayout(self.tab_2) - self.gridLayout_2.setObjectName("gridLayout_2") - self.edit_log = QtWidgets.QPlainTextEdit(self.tab_2) - self.edit_log.setTabChangesFocus(True) - self.edit_log.setObjectName("edit_log") - self.gridLayout_2.addWidget(self.edit_log, 0, 0, 1, 1) - self.tabWidget.addTab(self.tab_2, "") - self.verticalLayout.addWidget(self.tabWidget) - self.buttonBox = QtWidgets.QDialogButtonBox(Dialog) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.verticalLayout.addWidget(self.buttonBox) - - self.retranslateUi(Dialog) - self.tabWidget.setCurrentIndex(1) - self.buttonBox.accepted.connect(Dialog.accept) - self.buttonBox.rejected.connect(Dialog.reject) - QtCore.QMetaObject.connectSlotsByName(Dialog) - - def retranslateUi(self, Dialog): - _translate = QtCore.QCoreApplication.translate - Dialog.setWindowTitle(_translate("Dialog", "Review")) - self.label.setText(_translate("Dialog", "

Review the final report

")) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Dialog", "General")) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Dialog", "Log")) diff --git a/APP_CRASH/Appcrash/_forms/qcrash_rc.py b/APP_CRASH/Appcrash/_forms/qcrash_rc.py deleted file mode 100644 index ec8075bd5..000000000 --- a/APP_CRASH/Appcrash/_forms/qcrash_rc.py +++ /dev/null @@ -1,193 +0,0 @@ -# -*- coding: utf-8 -*- - -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore - -qt_resource_data = b"\ -\x00\x00\x06\xb2\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\ -\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ -\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ -\x79\x71\xc9\x65\x3c\x00\x00\x03\x24\x69\x54\x58\x74\x58\x4d\x4c\ -\x3a\x63\x6f\x6d\x2e\x61\x64\x6f\x62\x65\x2e\x78\x6d\x70\x00\x00\ -\x00\x00\x00\x3c\x3f\x78\x70\x61\x63\x6b\x65\x74\x20\x62\x65\x67\ -\x69\x6e\x3d\x22\xef\xbb\xbf\x22\x20\x69\x64\x3d\x22\x57\x35\x4d\ -\x30\x4d\x70\x43\x65\x68\x69\x48\x7a\x72\x65\x53\x7a\x4e\x54\x63\ -\x7a\x6b\x63\x39\x64\x22\x3f\x3e\x20\x3c\x78\x3a\x78\x6d\x70\x6d\ -\x65\x74\x61\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x3d\x22\x61\x64\x6f\ -\x62\x65\x3a\x6e\x73\x3a\x6d\x65\x74\x61\x2f\x22\x20\x78\x3a\x78\ -\x6d\x70\x74\x6b\x3d\x22\x41\x64\x6f\x62\x65\x20\x58\x4d\x50\x20\ -\x43\x6f\x72\x65\x20\x35\x2e\x33\x2d\x63\x30\x31\x31\x20\x36\x36\ -\x2e\x31\x34\x35\x36\x36\x31\x2c\x20\x32\x30\x31\x32\x2f\x30\x32\ -\x2f\x30\x36\x2d\x31\x34\x3a\x35\x36\x3a\x32\x37\x20\x20\x20\x20\ -\x20\x20\x20\x20\x22\x3e\x20\x3c\x72\x64\x66\x3a\x52\x44\x46\x20\ -\x78\x6d\x6c\x6e\x73\x3a\x72\x64\x66\x3d\x22\x68\x74\x74\x70\x3a\ -\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\ -\x39\x2f\x30\x32\x2f\x32\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\x74\ -\x61\x78\x2d\x6e\x73\x23\x22\x3e\x20\x3c\x72\x64\x66\x3a\x44\x65\ -\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20\x72\x64\x66\x3a\x61\x62\ -\x6f\x75\x74\x3d\x22\x22\x20\x78\x6d\x6c\x6e\x73\x3a\x78\x6d\x70\ -\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x6e\x73\x2e\x61\x64\x6f\x62\ -\x65\x2e\x63\x6f\x6d\x2f\x78\x61\x70\x2f\x31\x2e\x30\x2f\x22\x20\ -\x78\x6d\x6c\x6e\x73\x3a\x78\x6d\x70\x4d\x4d\x3d\x22\x68\x74\x74\ -\x70\x3a\x2f\x2f\x6e\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\ -\x2f\x78\x61\x70\x2f\x31\x2e\x30\x2f\x6d\x6d\x2f\x22\x20\x78\x6d\ -\x6c\x6e\x73\x3a\x73\x74\x52\x65\x66\x3d\x22\x68\x74\x74\x70\x3a\ -\x2f\x2f\x6e\x73\x2e\x61\x64\x6f\x62\x65\x2e\x63\x6f\x6d\x2f\x78\ -\x61\x70\x2f\x31\x2e\x30\x2f\x73\x54\x79\x70\x65\x2f\x52\x65\x73\ -\x6f\x75\x72\x63\x65\x52\x65\x66\x23\x22\x20\x78\x6d\x70\x3a\x43\ -\x72\x65\x61\x74\x6f\x72\x54\x6f\x6f\x6c\x3d\x22\x41\x64\x6f\x62\ -\x65\x20\x50\x68\x6f\x74\x6f\x73\x68\x6f\x70\x20\x43\x53\x36\x20\ -\x28\x4d\x61\x63\x69\x6e\x74\x6f\x73\x68\x29\x22\x20\x78\x6d\x70\ -\x4d\x4d\x3a\x49\x6e\x73\x74\x61\x6e\x63\x65\x49\x44\x3d\x22\x78\ -\x6d\x70\x2e\x69\x69\x64\x3a\x45\x35\x31\x37\x38\x41\x32\x41\x39\ -\x39\x41\x30\x31\x31\x45\x32\x39\x41\x31\x35\x42\x43\x31\x30\x34\ -\x36\x41\x38\x39\x30\x34\x44\x22\x20\x78\x6d\x70\x4d\x4d\x3a\x44\ -\x6f\x63\x75\x6d\x65\x6e\x74\x49\x44\x3d\x22\x78\x6d\x70\x2e\x64\ -\x69\x64\x3a\x45\x35\x31\x37\x38\x41\x32\x42\x39\x39\x41\x30\x31\ -\x31\x45\x32\x39\x41\x31\x35\x42\x43\x31\x30\x34\x36\x41\x38\x39\ -\x30\x34\x44\x22\x3e\x20\x3c\x78\x6d\x70\x4d\x4d\x3a\x44\x65\x72\ -\x69\x76\x65\x64\x46\x72\x6f\x6d\x20\x73\x74\x52\x65\x66\x3a\x69\ -\x6e\x73\x74\x61\x6e\x63\x65\x49\x44\x3d\x22\x78\x6d\x70\x2e\x69\ -\x69\x64\x3a\x45\x35\x31\x37\x38\x41\x32\x38\x39\x39\x41\x30\x31\ -\x31\x45\x32\x39\x41\x31\x35\x42\x43\x31\x30\x34\x36\x41\x38\x39\ -\x30\x34\x44\x22\x20\x73\x74\x52\x65\x66\x3a\x64\x6f\x63\x75\x6d\ -\x65\x6e\x74\x49\x44\x3d\x22\x78\x6d\x70\x2e\x64\x69\x64\x3a\x45\ -\x35\x31\x37\x38\x41\x32\x39\x39\x39\x41\x30\x31\x31\x45\x32\x39\ -\x41\x31\x35\x42\x43\x31\x30\x34\x36\x41\x38\x39\x30\x34\x44\x22\ -\x2f\x3e\x20\x3c\x2f\x72\x64\x66\x3a\x44\x65\x73\x63\x72\x69\x70\ -\x74\x69\x6f\x6e\x3e\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\x3e\ -\x20\x3c\x2f\x78\x3a\x78\x6d\x70\x6d\x65\x74\x61\x3e\x20\x3c\x3f\ -\x78\x70\x61\x63\x6b\x65\x74\x20\x65\x6e\x64\x3d\x22\x72\x22\x3f\ -\x3e\x9b\x84\x06\xb9\x00\x00\x03\x24\x49\x44\x41\x54\x78\xda\xc4\ -\x97\x6d\x68\x8d\x61\x18\xc7\xcf\x79\x4c\x33\xdb\x30\xd9\x41\x2b\ -\x8e\x88\x52\x23\x66\x3e\xf1\xc1\x49\x48\xda\xa9\xcd\x07\xf2\x91\ -\x79\xf9\xa0\xd4\x28\xf1\x01\xa9\x4d\x8d\x85\x14\x29\x5f\x94\x6f\ -\xe6\x35\xa5\x0c\x85\xa5\x30\x2b\xfb\x30\xc4\x96\xb1\x6c\x26\x6f\ -\x7b\x69\x5e\xe6\xf8\x5d\x75\x9d\x3c\x7b\xdc\xf7\xf1\x9c\xe3\x59\ -\xee\xfa\x77\x9d\x73\xbf\xfc\xaf\xff\x7d\x3d\xf7\xcb\x75\x87\x43\ -\x3e\xcb\xe4\xc2\xc8\x24\x4c\x1c\x2c\x03\xf3\xc0\x0c\x90\xa7\xcd\ -\x7d\xa0\x1d\x3c\x01\xb7\xc1\xe5\xee\x9e\x77\xef\xfd\xf0\x86\x7d\ -\x38\x5e\x80\xd9\x0d\xca\x41\x96\x4f\xbd\x3f\xc0\x05\x70\x08\x21\ -\xcd\x19\x09\xc0\xf1\x78\xcc\x61\xb0\xd1\x8f\x50\x4b\x49\x80\x33\ -\x60\x27\x42\x3e\xfb\x16\xa0\xb3\xae\xd7\x30\x07\x51\xe4\xf3\x54\ -\x98\xa2\x31\xca\xe0\x3c\x86\xb9\x0e\xa6\x84\x82\x2b\x05\x60\x7d\ -\x5e\x6e\xee\x83\xfe\x81\xfe\x76\x6b\x04\x70\x5e\x82\xb9\x05\xc6\ -\x85\x46\xa6\x7c\x01\x31\x22\xd1\xf4\x47\x04\x70\x2e\x2a\x6f\xca\ -\x4f\xd7\x80\x56\x70\x17\x14\x81\xec\x0c\x9c\x5d\xd3\xdf\x85\x6a\ -\x85\x63\x05\x91\x38\x4b\x24\x06\xa5\xc2\x71\x0d\x38\x02\xa2\x1e\ -\x92\x6a\xd4\x56\x60\x23\x60\x13\xe8\x76\x6d\xbb\x16\x70\x47\xd1\ -\xa2\x75\x21\xed\x23\x7d\x23\x3a\xb6\xda\xc3\x19\x55\x5f\xbf\x3f\ -\x01\xb3\x5f\x84\x79\x68\x98\x45\x09\x24\x8f\x3d\x3b\x63\x2a\x78\ -\x4e\xfd\x4f\xcf\xe7\x93\xc9\xcc\x06\x6f\xdd\x2b\x9e\xfa\x85\x98\ -\x26\x03\x77\x29\xfd\x1e\x25\xf7\xf5\x1e\x4b\x18\x87\x2d\x52\x25\ -\x36\x6e\x27\x15\xf4\xf4\x6f\x1c\xae\x22\x3e\xcb\x1d\x14\xca\x6a\ -\x2f\xb3\x74\x9a\x1b\xc0\xc2\xb3\x71\x94\x89\x6f\x47\x8f\x57\x93\ -\xca\x37\xe0\x6a\x00\x02\x84\xe3\xb5\x25\x32\x71\x11\x10\xb3\x0c\ -\xdc\x47\x58\x3f\xfc\xab\x77\xe5\xd8\x6f\x69\x8e\x89\x80\x62\x43\ -\xc3\x90\x9e\x84\x41\x95\x7a\xe5\xf4\x96\x62\xc7\xb0\xf5\xa4\x74\ -\xda\xce\xee\x0c\xa3\x20\x5c\x9d\x86\xa6\xa8\x08\xc8\x31\x34\x7c\ -\x1b\x81\x53\xf0\xbb\xa1\x2e\xc7\xb1\x5d\x86\x23\x20\x20\x62\xaa\ -\x14\x01\xbd\x86\xfa\x7c\xb6\x48\x24\x28\xcf\xca\x95\x6f\x68\xea\ -\x15\x01\x6d\x96\x71\xab\x02\x9c\xbd\x8d\xab\xcd\xb1\x1c\x93\x52\ -\xaa\x50\x9e\x15\xc0\xec\x85\xa3\xca\xd2\xdc\x24\x02\x1a\x2c\x8d\ -\x92\xf7\xd5\x06\x30\xfb\x5a\xe5\x32\x95\x86\x30\x0a\xe5\xdb\x74\ -\x81\xb1\x96\x4e\xe7\xc1\x76\xb6\x52\x57\x9a\x33\x97\x4b\xeb\x38\ -\x58\x6b\xe9\x32\x20\x49\x4f\xf2\x36\x3c\x89\xd9\xea\x52\x2c\xf7\ -\xf8\x41\xb0\xd4\xb5\x85\x2e\x81\x1b\x40\xd2\xaa\x66\x04\x0d\x19\ -\x42\x5d\x0a\xe6\xcb\x9d\x0f\xd6\x80\xd1\x29\x34\x9e\x82\x63\x5b\ -\x52\xc0\x34\xcc\x33\x30\x46\x13\xc9\x5d\xe0\x84\xde\xf5\x8b\x3d\ -\x03\x2f\x6a\x7e\x97\xf0\x08\x08\x6b\x5b\xdc\x47\x80\x24\x19\x99\ -\x03\x47\x87\xa3\x27\x55\x07\xa6\xc6\x95\x23\x48\x36\x2c\x89\xe9\ -\x3a\xc3\x45\x52\xe7\x75\xae\x1c\x52\x57\xe7\xf3\x0b\xd5\xa8\xcf\ -\x61\x19\x91\x64\x2e\x8d\xae\xff\x07\xe8\xd4\xae\x0b\x48\x32\x9c\ -\xbd\x60\x25\xb8\x9f\x82\xb8\xd5\x87\xf3\x46\x77\x96\xe4\x4d\x4a\ -\x25\x37\xb8\x07\x66\x6a\xd5\x0e\x44\x1c\x4b\x63\xe1\x4d\xc0\x7c\ -\x4c\xd1\xe5\x25\x58\xe2\x5e\xd0\x61\x03\xc9\x74\xdd\x9a\xb3\xb4\ -\x4a\x04\x5d\x01\x3d\x9a\xaa\x1f\x85\x60\x30\x03\x01\x2f\xc0\x72\ -\xc6\xbe\xf2\xf3\x30\x99\x88\x39\x67\x39\xc1\x0a\x20\xf9\x94\xa6\ -\x00\x79\x67\x6c\x30\xe5\x17\x4e\x8a\x24\x62\x35\xa8\xd4\x99\x67\ -\x5a\xe4\x81\xba\x59\xb8\x6c\xc9\x8d\x9f\xc7\xa9\xbc\x80\xb7\x28\ -\x24\x32\x45\x90\x7d\xb5\xf4\xcd\xd6\x7b\x5f\x9c\x9d\xd6\xbd\xde\ -\x97\xf2\x71\x9a\x48\x24\x42\xff\xb3\xfc\x12\x60\x00\xe6\x38\x0c\ -\x00\x42\x07\x69\x04\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\ -\x82\ -\x00\x00\x02\x93\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x20\x00\x00\x00\x20\x08\x04\x00\x00\x00\xd9\x73\xb2\x7f\ -\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ -\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ -\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ -\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x02\x20\x49\x44\ -\x41\x54\x48\xc7\x95\x95\xbd\x4b\x5b\x61\x14\xc6\x4f\x6e\x2f\xa4\ -\x42\x42\x62\xe8\x47\xba\x28\x6e\x4e\x85\x68\x84\x16\x8a\xa5\x0e\ -\xa5\x5b\x67\x41\xb0\x98\x8a\xdd\x3a\xa4\x85\xd2\xc1\x3f\xa0\x8b\ -\xa3\x83\xe0\x1e\xc5\x50\x27\xe9\x54\x6c\x5d\x3a\xb8\x64\xc8\xa0\ -\x92\x60\xbb\x48\x9b\x25\xa4\x2a\x24\x21\xbf\x0e\x49\xde\x7b\xee\ -\xf5\xe6\xde\xdb\xf3\x2e\xe7\x9c\xf7\x79\x9e\xfb\x7e\x9d\x73\x63\ -\x88\x8f\xdd\x91\x97\xf2\x4c\x1e\xca\x94\x24\x44\xe4\xaf\xd4\xa5\ -\x22\x5f\x65\x5f\x1a\x3e\x58\xbc\x23\x47\x89\x0e\x7e\xd6\xa1\x44\ -\xce\x8b\x77\x87\x29\xb6\xe8\x11\x64\x3d\xb6\x48\x8d\x12\xc8\x51\ -\x23\x8a\xd5\xf4\x3a\x1c\xfa\x02\xcd\x48\x74\x80\x26\x0b\x5e\x81\ -\xd9\xff\xa0\xf7\x25\x66\xb5\xc0\x38\xf5\xc1\x44\x95\xbd\x00\xa9\ -\x26\x7b\x54\x07\x7e\x9d\x71\x47\x60\xdb\x40\x96\x10\xe2\x14\xb8\ -\x00\x5a\x54\x38\xe4\x90\x0a\x2d\xe0\x82\x02\x71\x84\x25\x83\xdd\ -\x1e\x0a\xe4\xd5\x37\x66\xcc\x7d\x4c\x63\x99\xf3\xb1\x98\x36\x67\ -\x3f\xa3\xd0\xf9\xbe\x40\x59\xa5\xe6\x90\x90\x31\xa7\xd0\x65\x44\ -\xc8\xd2\x55\xa9\xe5\x50\x81\x65\x85\xee\x92\x15\xd6\x54\xe2\x17\ -\x99\x50\x81\x0c\x3f\x15\x63\x4d\x28\xa9\x70\x25\x94\x2e\x08\x2b\ -\x8a\x51\x12\x73\x2d\xd0\x75\x3f\xd2\x91\x23\xa5\x36\x5d\x15\xae\ -\x4c\x70\x1e\x89\x2e\x08\xe7\x86\x73\x65\xc9\x98\x29\xcc\xb6\x44\ -\xb5\x8e\xf1\xc6\x2c\x95\xbe\x1f\x59\xe0\x9e\xe3\x5a\xd2\x32\x7e\ -\x52\x4f\x04\xd2\x93\xc6\x6f\x59\x52\x53\x53\x2f\x22\x09\x68\x54\ -\xcd\x92\x63\x15\x16\xc5\x0e\xa5\xdb\x52\x54\xd1\xb1\xb0\xe8\xaa\ -\xb7\x8d\xd0\x1b\xd8\x70\xe1\x17\x85\x24\x97\xae\xd4\x2e\xd9\x91\ -\xe4\x07\xec\xba\xb0\x97\x24\x05\x61\x13\x80\x4f\x3c\xe5\x1b\x00\ -\x6d\x76\x58\x25\xcf\x2d\x43\xb4\x79\xcc\x1b\xca\xb4\x3d\xfd\x61\ -\xb3\x5f\x8d\x13\x5c\x03\x3d\x8a\xc4\xf9\xa1\x2a\x2d\x66\x04\x62\ -\x7c\xf6\x69\x2f\xd7\x4c\x0c\x1b\xca\xfa\x20\xf5\x88\x29\x53\x2a\ -\x4f\x5c\x8b\x9f\xf7\x11\x58\x77\x3a\x92\xcd\x11\x00\x5f\x10\xd2\ -\x14\xf8\xc8\x73\xb5\x01\x41\xb8\x7b\x83\x7e\x84\xad\x9b\x6a\x96\ -\x33\x00\xde\x8e\x38\xbe\xb4\x87\x7e\x36\x3c\x6a\x07\x32\xc9\x29\ -\x00\xdf\x79\xcf\x2b\x3e\x70\x3b\x40\xe0\x94\x49\xbf\x1f\x4b\x86\ -\x03\x05\x4a\x8f\x14\x38\xd0\x6d\xc7\xbd\xd0\x18\xaf\xf9\x1d\x28\ -\xf0\x87\x55\x75\x3b\x37\x04\x04\x21\x41\x91\x13\x1a\xc4\x5d\xd9\ -\x38\x0d\x4e\x78\x47\xc2\x8b\x8f\x11\xfa\xf8\x83\xed\x1f\xda\x5c\ -\xa2\xd6\x42\x8d\x25\x2c\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\ -\x60\x82\ -" - -qt_resource_name = b"\ -\x00\x02\ -\x00\x00\x07\x83\ -\x00\x72\ -\x00\x63\ -\x00\x0f\ -\x09\x7d\xcb\x07\ -\x00\x47\ -\x00\x69\x00\x74\x00\x48\x00\x75\x00\x62\x00\x2d\x00\x4d\x00\x61\x00\x72\x00\x6b\x00\x2e\x00\x70\x00\x6e\x00\x67\ -\x00\x15\ -\x0f\x0c\x2a\x07\ -\x00\x47\ -\x00\x69\x00\x74\x00\x48\x00\x75\x00\x62\x00\x2d\x00\x4d\x00\x61\x00\x72\x00\x6b\x00\x2d\x00\x4c\x00\x69\x00\x67\x00\x68\x00\x74\ -\x00\x2e\x00\x70\x00\x6e\x00\x67\ -" - -qt_resource_struct = b"\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ -\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\ -\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x00\x2e\x00\x00\x00\x00\x00\x01\x00\x00\x06\xb6\ -" - -def qInitResources(): - QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) - -def qCleanupResources(): - QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) - -qInitResources() diff --git a/APP_CRASH/Appcrash/_hooks.py b/APP_CRASH/Appcrash/_hooks.py deleted file mode 100644 index 79c7be75b..000000000 --- a/APP_CRASH/Appcrash/_hooks.py +++ /dev/null @@ -1,68 +0,0 @@ - -import logging -import sys -import traceback - -from PyQt5 import QtCore, QtWidgets - - -def _logger(): - return logging.getLogger(__name__) - - -try: - Signal = QtCore.pyqtSignal -except AttributeError: - Signal = QtCore.Signal - - -def fix_qt_imports(path): - with open(path, 'r') as f_script: - lines = f_script.read().splitlines() - new_lines = [] - for l in lines: - if l.startswith("import "): - l = "from . " + l - if "from PyQt5 import" in l: - l = l.replace("from PyQt5 import", "from qcrash.qt import") - new_lines.append(l) - with open(path, 'w') as f_script: - f_script.write("\n".join(new_lines)) - - -def except_hook(exc, tb): - from .api import show_report_dialog - title = '[Unhandled exception] %s: %s' % ( - exc.__class__.__name__, str(exc)) - msg_box = QtWidgets.QMessageBox() - msg_box.setWindowTitle('Unhandled exception') - msg_box.setText('An unhandled exception has occured...') - msg_box.setInformativeText( - 'Would you like to report the bug to the developers?') - msg_box.setIcon(msg_box.Critical) - msg_box.setDetailedText(tb) - msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok | - QtWidgets.QMessageBox.Cancel) - msg_box.button(msg_box.Ok).setText('Report') - msg_box.button(msg_box.Cancel).setText('Close') - if msg_box.exec_() == msg_box.Ok: - show_report_dialog(window_title='Report unhandled exception', - issue_title=title, traceback=tb) - - -class QtExceptHook(QtCore.QObject): - _report_exception_requested = Signal(object, object) - - def __init__(self, except_hook, *args, **kwargs): - super(QtExceptHook, self).__init__(*args, **kwargs) - sys.excepthook = self._except_hook - self._report_exception_requested.connect(except_hook) - - def _except_hook(self, exc_type, exc_val, tb): - tb = '\n'.join([''.join(traceback.format_tb(tb)), - '{0}: {1}'.format(exc_type.__name__, exc_val)]) - _logger().critical('unhandled exception:\n%s', tb) - # exception might come from another thread, use a signal - # so that we can be sure we will show the bug report dialog from - # the main gui thread. - self._report_exception_requested.emit(exc_val, tb) diff --git a/APP_CRASH/Appcrash/api.py b/APP_CRASH/Appcrash/api.py deleted file mode 100644 index 02299f56d..000000000 --- a/APP_CRASH/Appcrash/api.py +++ /dev/null @@ -1,122 +0,0 @@ -""" -This module contains the top level API functions. -""" -from . import _hooks -from PyQt5 import QtCore - -from . import backends - - -def install_backend(*args): - """ - Install one or more backends. - - Usage:: - - qcrash.install_backend(backend1) - qcrash.install_backend(backend2, backend3) - - :param args: the backends to install. Each backend must be a subclass - of :class:`qcrash.backends.BaseBackend` (e.g.:: - :class:`qcrash.backends.EmailBackend` or - :class:`qcrash.backends.GithubBackend`) - """ - global _backends - for b in args: - _backends.append(b) - - -def get_backends(): - """ - Gets the list of installed backends. - """ - return _backends - - -def install_except_hook(except_hook=_hooks.except_hook): - """ - Install an except hook that will show the crash report dialog when an - unhandled exception has occured. - - :param except_hook: except_hook function that will be called on the main - thread whenever an unhandled exception occured. The function takes - two parameters: the exception object and the traceback string. - """ - if not _backends: - raise ValueError('no backends found, you must at least install one ' - 'backend before calling this function') - global _except_hook - _except_hook = _hooks.QtExceptHook(except_hook) - - -def set_qsettings(qsettings): - """ - Sets the qsettings used by the backends to cache some information such - as the user credentials. If no custom qsettings is defined, qcrash will - use its own settings (QSettings('QCrash')) - - :param qsettings: QtCore.QSettings instance - """ - global _qsettings - _qsettings = qsettings - - -def show_report_dialog(window_title='Report an issue...', - window_icon=None, traceback=None, issue_title='', - issue_description='', parent=None, - modal=None, include_log=True, include_sys_info=True): - """ - Show the issue report dialog manually. - - :param window_title: Title of dialog window - :param window_icon: the icon to use for the dialog window - :param traceback: optional traceback string to include in the report. - :param issue_title: optional issue title - :param issue_description: optional issue description - :param parent: parent widget - :param include_log: Initial state of the include log check box - :param include_sys_info: Initial state of the include system info check box - """ - if not _backends: - raise ValueError('no backends found, you must at least install one ' - 'backend before calling this function') - from ._dialogs.report import DlgReport - dlg = DlgReport(_backends, window_title=window_title, - window_icon=window_icon, traceback=traceback, - issue_title=issue_title, - issue_description=issue_description, parent=parent, - include_log=include_log, include_sys_info=include_sys_info) - if modal: - dlg.show() - return dlg - else: - dlg.exec_() - - -def _return_empty_string(): - return '' - - -#: Reference to the function to use to collect system information. Client code -#: should redefine it. -get_system_information = _return_empty_string - -#: Reference to the function to use to collect the application's log. -#: Client code should redefine it. -get_application_log = _return_empty_string - - -_backends = [] -_qsettings = QtCore.QSettings('QCrash') - - -__all__ = [ - 'backends', - 'install_backend', - 'get_backends', - 'install_except_hook', - 'set_qsettings', - 'show_report_dialog', - 'get_application_log', - 'get_system_information', -] diff --git a/APP_CRASH/Appcrash/backends/GITHUB.py b/APP_CRASH/Appcrash/backends/GITHUB.py deleted file mode 100644 index 919f3367b..000000000 --- a/APP_CRASH/Appcrash/backends/GITHUB.py +++ /dev/null @@ -1,205 +0,0 @@ -""" -This module contains the github backend. -""" -import logging -import webbrowser -#import requests -#import keyring -#import json -from .base import BaseBackend -from ..formatters.markdown import MardownFormatter -from PyQt5 import QtGui, QtCore, QtWidgets -from .._dialogs.gh_login import DlgGitHubLogin -import github - -GH_MARK_NORMAL = ':/rc/GitHub-Mark.png' -GH_MARK_LIGHT = ':/rc/GitHub-Mark-Light.png' - - -def _logger(): - return logging.getLogger(__name__) - - -class GithubBackend(BaseBackend): - - def __init__(self, gh_owner, gh_repo, formatter=MardownFormatter()): - """ - :param gh_owner: Name of the owner of the github repository. - :param gh_repo: Name of the repository on github. - """ - super(GithubBackend, self).__init__( - formatter, "Submit on github", - "Submit the issue on our issue tracker on github", None) - icon = GH_MARK_NORMAL - if QtWidgets.qApp.palette().base().color().lightness() < 128: - icon = GH_MARK_LIGHT - self.button_icon = QtGui.QIcon(icon) - self.gh_owner = gh_owner - self.gh_repo = gh_repo - self._show_msgbox = True # False when running the test suite - - def send_report(self, title, body, application_log=None): - _logger().debug('sending bug report on github\ntitle=%s\nbody=%s', - title, body) - username, password, remember, is_basic, remember_token, token = self.get_user_credentials() - _logger().debug('got user credentials') - - try: - ''' - print('username is',username) - print('pass is',password) - print('is basic',is_basic) - print('remember token',remember_token) - print('token is',token)''' - - if not is_basic: - gh = github.Github(username) - else: - gh = github.Github(username,password) - # upload log file as a gist - if application_log: - url = self.upload_log_file(application_log,gh,username, is_basic) - body += '\nApplication log: %s' % url - if url is None: - return False - repo = gh.get_repo(str(self.gh_owner)+'/'+str(self.gh_repo)) - ret = repo.create_issue(title=title, body=body) - '''labels = repo.get_labels() - for x in labels: - print(x) - print(repo.get_topics(),'got this')''' - except Exception as e: - QtWidgets.qApp.restoreOverrideCursor() - _logger().warn('failed to send bug report on github. %s' % type(e).__name__) - # invalid credentials - if e.status == 401: - if is_basic: - self.qsettings().setValue('github/remember_credentials', 0) - else: - self.qsettings().setValue('github/remember_token', 0) - if self._show_msgbox: - QtWidgets.QMessageBox.warning( - self.parent_widget, 'Invalid credentials', - 'Failed to create github issue, invalid credentials...') - else: - # other issue - if self._show_msgbox: - QtWidgets.QMessageBox.warning( - self.parent_widget, - 'Failed to create issue', - 'Failed to create github issue. Error %s' % - type(e).__name__+"\n \n"+"NOTE: If you are using Two Factor Authentication. Please Sign in using Access Token.") - return False - else: - #issue_nbr = ret['number'] - issue_nbr = ret.number - if self._show_msgbox: - ret = QtWidgets.QMessageBox.question( - self.parent_widget, 'Issue created on github', - 'Issue successfully created. Would you like to open the ' - 'ticket in your web browser?') - if ret in [QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.Ok]: - webbrowser.open( - 'https://github.com/%s/%s/issues/%d' % ( - self.gh_owner, self.gh_repo, issue_nbr)) - return True - - def _get_credentials_from_qsettings(self): - remember = self.qsettings().value('github/remember_credentials', "0") - username = self.qsettings().value('github/username', "") - password = self.qsettings().value('github/password', "") - remember_token = self.qsettings().value('github/remember_token', "0") - token = self.qsettings().value('github/token', "") - try: - # PyQt5 or PyQt4 api v2 - remember = bool(int(remember)) - remember_token = bool(int(remember_token)) - - except TypeError: # pragma: no cover - # pyside returns QVariants - remember, _ok = remember.toInt() - remember_token, _ok = remember_token.toInt() - username = username.toString() - password = password.toString() - token = token.toString() - - if not remember: - username = '' - password = '' - if not remember_token: - token = '' - return username, bool(remember), password, remember_token, token - - def _store_credentials(self, username, password, remember, is_basic, remember_token, token): - if is_basic: - self.qsettings().setValue('github/username', username) - self.qsettings().setValue('github/password', password) - self.qsettings().setValue('github/remember_credentials', int(remember)) - else: - self.qsettings().setValue('github/remember_token', int(remember_token)) - self.qsettings().setValue('github/token', token) - - - - def get_user_credentials(self): # pragma: no cover - # reason: hard to test methods that shows modal dialogs - username, remember, password, remember_token, token = self._get_credentials_from_qsettings() - '''print('username is',username) - print('pass is',password) - print('remember token',remember_token) - print('token is',token) - print('llllllllllllllllllllllll')''' - # ask for credentials - username, password, remember, is_basic, remember_token, token = DlgGitHubLogin.login( - self.parent_widget, username, remember, password, remember_token, token) - '''print('username is',username) - print('pass is',password) - print('remember token',remember_token) - print('remember is',remember) - print('token is',token) - print('is_basic',is_basic) - print('llllllllllllllllllllllll jjjjjjjjjjjjjjjjjjjjjjj' )''' - self._store_credentials(username, password, remember, is_basic, remember_token, token) - - return username, password, remember, is_basic, remember_token, token - - def upload_log_file(self, log_content, gh, username, is_basic): - try: - QtWidgets.qApp.setOverrideCursor(QtCore.Qt.WaitCursor) - '''data = { - "public": True, - "files": { - "Osdag_crash_log.log": { - "content": log_content - }, - } - } - if is_basic: - auth = gh.get_user() - ret = auth.create_gist(True, {"Osdag_crash_log.log": github.InputFileContent(log_content)},"Osdag crash report.") - ret = str(ret.id) - ret = 'https://gist.github.com/' + ret - else: - query_url = "https://api.github.com/gists" - headers = {'Authorization': f'token {username}'} - r = requests.post(query_url, headers=headers, data=json.dumps(data)) - ret = r.json() - ret = ret['html_url']''' - auth = gh.get_user() - ret = auth.create_gist(True, {"Osdag_crash_log.log": github.InputFileContent(log_content)},"Osdag crash report.") - ret = str(ret.id) - ret = 'https://gist.github.com/' + ret - QtWidgets.qApp.restoreOverrideCursor() - except Exception as e: - - QtWidgets.qApp.restoreOverrideCursor() - QtWidgets.QMessageBox.warning( - self.parent_widget, 'Error', - 'Unable to create gist. {}'.format(type(e).__name__)+"\n \n"+"NOTE: If you are using Two Factor Authentication. Please Sign in using Access Token.") - if is_basic: - self.qsettings().setValue('github/remember_credentials', 0) - else: - self.qsettings().setValue('github/remember_token', 0) - return None - else: - return ret diff --git a/APP_CRASH/Appcrash/backends/__init__.py b/APP_CRASH/Appcrash/backends/__init__.py deleted file mode 100644 index 1a494a493..000000000 --- a/APP_CRASH/Appcrash/backends/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This package contains the backends used to actually send the crash report. - -QCrash provides 2 backends: - -- email: let the user report the crash/issue via an email -- github: let the user report the crash/issue on you github issue tracker - -To use a backend, use :meth:`qcrash.api.install_backend`. - -To add your own, just subclass :class:`qcrash.backends.BaseBackend` and -implement `send_report`. -""" -from .base import BaseBackend -from .email import EmailBackend -from .GITHUB import GithubBackend - - -__all__ = [ - 'BaseBackend', - 'EmailBackend', - 'GithubBackend' -] diff --git a/APP_CRASH/Appcrash/backends/base.py b/APP_CRASH/Appcrash/backends/base.py deleted file mode 100644 index 5e2e81cc2..000000000 --- a/APP_CRASH/Appcrash/backends/base.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -This module contains the base class for implementing a report backend. -""" - - -class BaseBackend(object): - """ - Base class for implementing a backend. - - Subclass must define ``button_text``, ``button_tooltip``and ``button_icon`` - and implement ``send_report(title, description)``. - - The report's title and body will be formatted automatically by the - associated :attr:`formatter`. - """ - def __init__(self, formatter, button_text, button_tooltip, - button_icon=None, need_review=True): - """ - :param formatter: the associated formatter (see :meth:`set_formatter`) - :param button_text: Text of the associated button in the report dialog - :param button_icon: Icon of the associated button in the report dialog - :param button_tooltip: Tooltip of the associated button in the report - dialog - :param need_review: True to show the review dialog before submitting. - Some backends (such as the email backend) do not need a review - dialog as the user can already review it before sending the final - report - """ - self.formatter = formatter - self.button_text = button_text - self.button_tooltip = button_tooltip - self.button_icon = button_icon - self.need_review = need_review - self.parent_widget = None - - def qsettings(self): - """ - Gets the qsettings instance that you can use to store various settings - such as the user credentials (you should use the `keyring` module if - you want to store user's password). - """ - from ..api import _qsettings - return _qsettings - - def set_formatter(self, formatter): - """ - Sets the formatter associated with the backend. - - The formatter will automatically get called to format the report title - and body before ``send_report`` is being called. - """ - self.formatter = formatter - - def send_report(self, title, body, application_log=None): - """ - Sends the actual bug report. - - :param title: title of the report, already formatted. - :param body: body of the reporit, already formtatted. - :param application_log: Content of the application log. Default is None. - - :returns: Whether the dialog should be closed. - """ - raise NotImplementedError diff --git a/APP_CRASH/Appcrash/backends/email.py b/APP_CRASH/Appcrash/backends/email.py deleted file mode 100644 index 3bb1d279a..000000000 --- a/APP_CRASH/Appcrash/backends/email.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -This module containes the email backend. -""" -from .base import BaseBackend -from ..formatters.email import EmailFormatter -from PyQt5 import QtCore, QtGui - - -class EmailBackend(BaseBackend): - """ - This backend sends the crash report via email (using mailto). - - Usage:: - - email_backend = qcrash.backends.EmailBackend( - 'your_email@provider.com', 'YourAppName') - qcrash.install_backend(email_backend) - - """ - def __init__(self, email, app_name, formatter=EmailFormatter()): - """ - :param email: email address to send the bug report to - :param app_name: application name, will appear in the object of the - mail - :param formatter: the formatter to use to create the final report. - """ - super(EmailBackend, self).__init__( - formatter, "Send email", "Send the report via email", - QtGui.QIcon.fromTheme('mail-send'), need_review=False) - self.formatter.app_name = app_name - self.email = email - - def send_report(self, title, body, application_log=None): - base_url = "mailto:%s?subject=%s" % (self.email, title) - if application_log: - body += "\nApplication log\n----------------\n\n%s" % application_log - base_url += '&body=%s' % body - url = QtCore.QUrl(base_url) - QtGui.QDesktopServices.openUrl(url) - return False diff --git a/APP_CRASH/Appcrash/formatters/base.py b/APP_CRASH/Appcrash/formatters/base.py deleted file mode 100644 index 17ed1f9fd..000000000 --- a/APP_CRASH/Appcrash/formatters/base.py +++ /dev/null @@ -1,25 +0,0 @@ -class BaseFormatter(object): - """ - Base class for implementing a custom formatter. - - Just implement :meth:`format_body` and :meth:`format_title` functions and - set your formatter on the backends you created. - """ - def format_title(self, title): - """ - Formats the issue title. By default this method does nothing. - - An email formatter might want to append the application to name to - the object field... - """ - return title - - def format_body(self, description, sys_info=None, traceback=None): - """ - Not implemented. - - :param description: Description of the issue, written by the user. - :param sys_info: Optional system information string - :param traceback: Optional traceback. - """ - raise NotImplementedError diff --git a/APP_CRASH/Appcrash/formatters/email.py b/APP_CRASH/Appcrash/formatters/email.py deleted file mode 100644 index 52e7a5915..000000000 --- a/APP_CRASH/Appcrash/formatters/email.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -This module contains the Html formatter used by the email backend. -""" -from .base import BaseFormatter - - -BODY_ITEM_TEMPLATE = '''%(name)s -%(delim)s - -%(value)s - - -''' - -NB_LINES_MAX = 50 - - -class EmailFormatter(BaseFormatter): - """ - Formats the crash report for use in an email (text/plain) - """ - def __init__(self, app_name=None): - """ - :param app_name: Name of the application. If set the email subject will - starts with [app_name] - """ - self.app_name = app_name - - def format_title(self, title): - """ - Formats title (add ``[app_name]`` if app_name is not None). - """ - if self.app_name: - return '[%s] %s' % (self.app_name, title) - return title - - def format_body(self, description, sys_info=None, traceback=None): - """ - Formats the body in plain text. (add a series of '-' under each section - title). - - :param description: Description of the issue, written by the user. - :param sys_info: Optional system information string - :param log: Optional application log - :param traceback: Optional traceback. - """ - name = 'Description' - delim = '-' * 40 - body = BODY_ITEM_TEMPLATE % { - 'name': name, 'value': description, 'delim': delim - } - if traceback: - name = 'Traceback' - traceback = '\n'.join(traceback.splitlines()[-NB_LINES_MAX:]) - body += BODY_ITEM_TEMPLATE % { - 'name': name, 'value': traceback, 'delim': delim - } - if sys_info: - name = 'System information' - body += BODY_ITEM_TEMPLATE % { - 'name': name, 'value': sys_info, 'delim': delim - } - return body diff --git a/APP_CRASH/Appcrash/formatters/markdown.py b/APP_CRASH/Appcrash/formatters/markdown.py deleted file mode 100644 index 7d107bac3..000000000 --- a/APP_CRASH/Appcrash/formatters/markdown.py +++ /dev/null @@ -1,40 +0,0 @@ - -from .base import BaseFormatter - - -BODY_ITEM_TEMPLATE = '''### %(name)s - -%(value)s - -''' - -NB_LINES_MAX = 50 - - -class MardownFormatter(BaseFormatter): - """ - Formats the issue report using Markdown. - """ - def format_body(self, description, sys_info=None, traceback=None): - """ - Formats the body using markdown. - - :param description: Description of the issue, written by the user. - :param sys_info: Optional system information string - :param log: Optional application log - :param traceback: Optional traceback. - """ - body = BODY_ITEM_TEMPLATE % { - 'name': 'Description', 'value': description - } - if traceback: - traceback = '\n'.join(traceback.splitlines()[-NB_LINES_MAX:]) - body += BODY_ITEM_TEMPLATE % { - 'name': 'Traceback', 'value': '```\n%s\n```' % traceback - } - if sys_info: - sys_info = '- %s' % '\n- '.join(sys_info.splitlines()) - body += BODY_ITEM_TEMPLATE % { - 'name': 'System information', 'value': sys_info - } - return body diff --git a/APP_CRASH/Appcrash/qt.py b/APP_CRASH/Appcrash/qt.py deleted file mode 100644 index 55223ecad..000000000 --- a/APP_CRASH/Appcrash/qt.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging -import sys - -_logger = logging.getLogger(__name__) - -try: - from PyQt5 import QtWidgets, QtGui, QtCore -except (ImportError, RuntimeError): - _logger.warning('failed to import PyQt5, going to try PyQt4') - try: - from PyQt4 import QtGui, QtGui as QtWidgets, QtCore - except (ImportError, RuntimeError): - _logger.warning('failed to import PyQt4, going to try PySide') - try: - from PySide import QtGui, QtCore, QtGui as QtWidgets - except (ImportError, RuntimeError): - _logger.warning('failed to import PySide') - _logger.critical('No Qt bindings found, aborting...') - try: - from unittest.mock import MagicMock - QtCore = MagicMock() - QtGui = MagicMock() - QtWidgets = MagicMock() - except ImportError: - sys.exit(1) - -__all__ = ['QtCore', 'QtGui', 'QtWidgets'] diff --git a/APP_CRASH/forms/dlg_github_login.ui b/APP_CRASH/forms/dlg_github_login.ui deleted file mode 100644 index ea1fbe1d5..000000000 --- a/APP_CRASH/forms/dlg_github_login.ui +++ /dev/null @@ -1,105 +0,0 @@ - - - Dialog - - - - 0 - 0 - 366 - 248 - - - - - 350 - 0 - - - - Sign in to github - - - - - - <html><head/><body><p align="center"><img src=":/rc/GitHub-Mark.png"/></p><p align="center">Sign in to GitHub</p></body></html> - - - - - - - 0 - - - - - Username: - - - - - - - - - - Password: - - - - - - - QLineEdit::Password - - - - - - - - - Remember me - - - - - - - Remember password - - - - - - - Sign in - - - - - - - - - - - cb_remember - toggled(bool) - cb_remember_password - setEnabled(bool) - - - 199 - 136 - - - 199 - 164 - - - - - diff --git a/APP_CRASH/forms/dlg_report_bug.ui b/APP_CRASH/forms/dlg_report_bug.ui deleted file mode 100644 index 0dc0992db..000000000 --- a/APP_CRASH/forms/dlg_report_bug.ui +++ /dev/null @@ -1,101 +0,0 @@ - - - Dialog - - - - 0 - 0 - 544 - 272 - - - - Report an issue... - - - - - - QFormLayout::ExpandingFieldsGrow - - - - - Title: - - - - - - - Title of the issue - - - - - - - Description: - - - - - - - Description of the issue (mandatory) - - - true - - - - - - - Include system information - - - Include system &information - - - - - - - Include application log - - - Include application &log - - - - - - - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - diff --git a/APP_CRASH/forms/dlg_review.ui b/APP_CRASH/forms/dlg_review.ui deleted file mode 100644 index e9c60f8bb..000000000 --- a/APP_CRASH/forms/dlg_review.ui +++ /dev/null @@ -1,119 +0,0 @@ - - - Dialog - - - - 0 - 0 - 622 - 495 - - - - Review - - - - .. - - - - - - - - - background-color:palette(highlight) ; -color: palette(highlighted-text); -padding: 10px; -border-radius:3px; - - - <html><head/><body><p align="center">Review the final report</p></body></html> - - - - - - - 1 - - - - General - - - - - - true - - - - - - - - Log - - - - - - true - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - Dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/APP_CRASH/forms/qcrash.qrc b/APP_CRASH/forms/qcrash.qrc deleted file mode 100644 index 3e9decd7e..000000000 --- a/APP_CRASH/forms/qcrash.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - - rc/GitHub-Mark-Light.png - rc/GitHub-Mark.png - - diff --git a/APP_CRASH/forms/rc/GitHub-Mark-Light.png b/APP_CRASH/forms/rc/GitHub-Mark-Light.png deleted file mode 100644 index 0aa1060df..000000000 Binary files a/APP_CRASH/forms/rc/GitHub-Mark-Light.png and /dev/null differ diff --git a/APP_CRASH/forms/rc/GitHub-Mark.png b/APP_CRASH/forms/rc/GitHub-Mark.png deleted file mode 100644 index 8b25551a9..000000000 Binary files a/APP_CRASH/forms/rc/GitHub-Mark.png and /dev/null differ diff --git a/Command_line.py b/Command_line.py deleted file mode 100644 index 1f54474e0..000000000 --- a/Command_line.py +++ /dev/null @@ -1,248 +0,0 @@ - - -from pathlib import Path -import os -import errno -import sys -import yaml -from utils.common.component import Bolt, Plate, Weld -from Common import * - - - -############################ Pre-Build Database Updation/Creation ################# -sqlpath = Path('ResourceFiles/Database/Intg_osdag.sql') -sqlitepath = Path('ResourceFiles/Database/Intg_osdag.sqlite') - -if sqlpath.exists(): - if not sqlitepath.exists(): - cmd = 'sqlite3 ' + str(sqlitepath) + ' < ' + str(sqlpath) - os.system(cmd) - sqlpath.touch() - print('Database Created') - - elif sqlitepath.stat().st_size == 0 or sqlitepath.stat().st_mtime < sqlpath.stat().st_mtime - 1: - try: - sqlitenewpath = Path('ResourceFiles/Database/Intg_osdag_new.sqlite') - cmd = 'sqlite3 ' + str(sqlitenewpath) + ' < ' + str(sqlpath) - error = os.system(cmd) - print(error) - # if error != 0: - # raise Exception('SQL to SQLite conversion error 1') - # if sqlitenewpath.stat().st_size == 0: - # raise Exception('SQL to SQLite conversion error 2') - os.remove(sqlitepath) - sqlitenewpath.rename(sqlitepath) - sqlpath.touch() - print('Database Updated', sqlpath.stat().st_mtime, sqlitepath.stat().st_mtime) - except Exception as e: - sqlitenewpath.unlink() - print('Error: ', e) -######################################################################################### - - - - - - -from design_type.connection.fin_plate_connection import FinPlateConnection -from design_type.connection.cleat_angle_connection import CleatAngleConnection -from design_type.connection.seated_angle_connection import SeatedAngleConnection -from design_type.connection.end_plate_connection import EndPlateConnection -from design_type.connection.base_plate_connection import BasePlateConnection - -from design_type.connection.beam_cover_plate import BeamCoverPlate -from design_type.connection.beam_cover_plate_weld import BeamCoverPlateWeld -from design_type.connection.column_cover_plate_weld import ColumnCoverPlateWeld - -from design_type.tension_member.tension_bolted import Tension_bolted -from design_type.tension_member.tension_welded import Tension_welded -from design_type.connection.beam_end_plate import BeamEndPlate -from design_type.connection.column_cover_plate import ColumnCoverPlate -from design_type.connection.column_end_plate import ColumnEndPlate -from design_type.compression_member.compression import Compression - - - -all_modules = {'Base Plate':BasePlateConnection, 'Beam Coverplate Weld Connection':BeamCoverPlateWeld,'Beam Coverplate Connection':BeamCoverPlate, - 'Cleat Angle':CleatAngleConnection, 'Column Coverplate Weld Connection':ColumnCoverPlateWeld, 'Column Coverplate Connection':ColumnCoverPlate, - 'Column Endplate Connection':ColumnEndPlate, 'End Plate':EndPlateConnection, 'Fin Plate':FinPlateConnection,'Seated Angle': SeatedAngleConnection, - 'Tension Members Bolted Design':Tension_bolted, 'Tension Members Welded Design':Tension_welded, 'Compression Member':Compression, - } - -available_module = {'Beam Coverplate Weld Connection':BeamCoverPlateWeld,'Beam Coverplate Connection':BeamCoverPlate, - 'Cleat Angle':CleatAngleConnection, 'Column Coverplate Weld Connection':ColumnCoverPlateWeld, 'Column Coverplate Connection':ColumnCoverPlate, - 'Column Endplate Connection':ColumnEndPlate, 'End Plate':EndPlateConnection, 'Fin Plate':FinPlateConnection,'Seated Angle': SeatedAngleConnection, - 'Tension Members Bolted Design':Tension_bolted, 'Tension Members Welded Design':Tension_welded, 'Compression Member':Compression, - } - - -def make_sure_path_exists(path): # Works on all OS. - try: - os.makedirs(path) - except OSError as exception: - if exception.errno != errno.EEXIST: - raise - - - -input_file_path = os.path.join(os.path.dirname(__file__), 'ResourceFiles', 'design_example') - -output_file_path = os.path.join(os.path.dirname(__file__), 'OUTPUT_FILES', 'Command_line_output') - - -make_sure_path_exists(output_file_path) #make sure output folder exists if not then create. - - - -osi_files = [file for file in os.listdir(input_file_path) if file.endswith(".osi")] - -files_data = [] # list of tuples in which the first item will be file name and second item will be data of that file in dictionary format. - -def precompute_data(): - - for file in osi_files: - - in_file = input_file_path + '/' + file - - with open(in_file, 'r') as fileObject: - - uiObj = yaml.load(fileObject, yaml.Loader) - - files_data.append((file, uiObj)) - - - -def create_files(): - - for file in files_data: - - data = file[1] - module = data['Module'] - file_name = file[0].split(".")[0] - file_name += ".txt" - - if module in available_module: - - main = available_module[module] - main.set_osdaglogger(None) - main.set_input_values(main, data) - - # output_dict = main.results_to_test(main) - # - # path = os.path.join(output_file_path, file_name) - # - # with open(path, "w") as content: - # content.write(str(output_dict)) - -#Block print -def blockPrint(): - sys.stdout = open(os.devnull, 'w') - -# Restore print -def enablePrint(): - sys.stdout = sys.__stdout__ - -# <<<<<<< HEAD -# module = d['Module'] -# if module == 'Fin Plate': -# main = FinPlateConnection -# main.set_osdaglogger(None) -# main.set_input_values(main, d) -# base = os.path.basename(f) -# filename = str(os.path.splitext(base)[0])+".txt" -# main.results_to_test(main, os.path.basename(filename)) -# elif module == 'Tension Members Bolted Design': -# main = Tension_bolted -# main.set_osdaglogger(None) -# main.set_input_values(main, d) -# base = os.path.basename(f) -# filename = str(os.path.splitext(base)[0]) + ".txt" -# main.results_to_test(main, os.path.basename(filename)) -# elif module == 'Tension Members Welded Design': -# main = Tension_welded -# main.set_osdaglogger(None) -# main.set_input_values(main, d) -# base = os.path.basename(f) -# filename = str(os.path.splitext(base)[0]) + ".txt" -# main.results_to_test(main, os.path.basename(filename)) -# # elif module == 'Column Coverplate Connection': -# # self = ColumnCoverPlate -# # self.set_osdaglogger() -# # self.set_input_values(self, d) -# ======= -if __name__ == '__main__': - - blockPrint() - - precompute_data() - - create_files() - -#writer = pd.ExcelWriter(workbook_name, engine='openpyxl') -# writer.book = wb -# test_in_list, test_out_list = main.results_to_test(main) -# -# # df = pd.DataFrame.from_records(list(test_out_list.items()), columns=['Check', 'Value']) -# -# input_keys = list(test_in_list.keys()) -# input_values = list(map(str, list(test_in_list.values()))) -# output_keys = list(test_out_list.keys()) -# output_values = list(map(str, list(test_out_list.values()))) -# -# while len(input_keys) != len(output_keys): -# if len(input_keys) < len(output_keys): -# for i in range(0,len(output_keys)-len(input_keys)): -# input_keys.append('') -# input_values.append('') -# else: -# for i in range(0,len(output_keys)-len(input_keys)): -# output_keys.append('') -# output_values.append('') -# -# sheet_name_as_list = [sheet_name] -# for i in range(0, len(input_keys)): -# sheet_name_as_list.append('') -# -# for row in zip(sheet_name_as_list,input_keys,input_values,output_keys,output_values): -# wb.active.append(row) -# -# # df.to_excel(writer, sheet_name=sheet_name, index=False) -# writer.save() -# writer.close() -# -# elif module == KEY_DISP_TENSION_BOLTED: -# print('filenAME', f) -# main = Tension_bolted -# main.set_osdaglogger(None) -# main.set_input_values(main, d) -# -# elif module == KEY_DISP_TENSION_WELDED: -# print('filenAME', f) -# main = Tension_welded -# main.set_osdaglogger(None) -# main.set_input_values(main, d) -# -# elif module == KEY_DISP_COLUMNCOVERPLATE: -# print('filenAME', f) -# main = ColumnCoverPlate -# main.set_osdaglogger(None) -# main.set_input_values(main, d) -# -# elif module == KEY_DISP_COLUMNCOVERPLATEWELD: -# print('filenAME', f) -# main = ColumnCoverPlateWeld -# main.set_osdaglogger(None) -# main.set_input_values(main, d) -# -# elif module == KEY_DISP_BEAMCOVERPLATE: -# print('filenAME', f) -# main = BeamCoverPlate -# main.set_osdaglogger(None) -# main.set_input_values(main, d) -# -# elif module == KEY_DISP_BEAMCOVERPLATEWELD: -# print('filenAME', f) -# main = BeamCoverPlateWeld -# main.set_osdaglogger(None) -# main.set_input_values(main, d) diff --git a/Common.py b/Common.py deleted file mode 100644 index 65892293d..000000000 --- a/Common.py +++ /dev/null @@ -1,2147 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# @author: Amir, Umair, Arsil - -import operator -import math -from utils.common.other_standards import * -import os - -PATH_TO_DATABASE = os.path.join(os.path.dirname(__file__), 'ResourceFiles', 'Database', 'Intg_osdag.sqlite') - - -import sqlite3 - -from utils.common.component import * -from utils.common.component import * -import logging -# from design_type.connection.fin_plate_connection import FinPlateConnection -# from design_type.connection.column_cover_plate import ColumnCoverPlate - -class OurLog(logging.Handler): - - def __init__(self, key): - logging.Handler.__init__(self) - - self.key = key - # self.key.setText("

Welcome to Osdag

") - - def handle(self, record): - msg = self.format(record) - if record.levelname == 'WARNING': - msg = ""+ msg +"" - elif record.levelname == 'ERROR': - msg = ""+ msg +"" - elif record.levelname == 'INFO': - msg = "" + msg + "" - self.key.append(msg) - - -def connectdb1(): - """ - Function to fetch diameter values from Bolt Table - """ - # @author: Amir - - lst = [] - conn = sqlite3.connect(PATH_TO_DATABASE) - cursor = conn.execute("SELECT Bolt_diameter FROM Bolt") - rows = cursor.fetchall() - for row in rows: - lst.append(row) - l2 = tuple_to_str_popup(lst) - return l2 - -def connectdb2(): - """ - Function to fetch diameter values from Bolt Table - """ - # @author: Amir - - lst = [] - conn = sqlite3.connect(PATH_TO_DATABASE) - cursor = conn.execute("SELECT Diameter FROM Anchor_Bolt") - rows = cursor.fetchall() - for row in rows: - lst.append(row) - l2 = tuple_to_str_popup(lst) - return l2 - - -def connectdb(table_name, call_type="dropdown"): - - """ - Function to fetch designation values from respective Tables. - """ - - # @author: Amir - conn = sqlite3.connect(PATH_TO_DATABASE) - lst = [] - if table_name == "Angles": - cursor = conn.execute("SELECT Designation FROM Angles") - - elif table_name == "Channels": - cursor = conn.execute("SELECT Designation FROM Channels") - - elif table_name == "Beams": - cursor = conn.execute("SELECT Designation FROM Beams") - - elif table_name == "Bolt": - cursor = conn.execute("SELECT Bolt_diameter FROM Bolt") - - elif table_name == "Material": - cursor = conn.execute("SELECT Grade FROM Material") - - elif table_name == "RHS": - cursor = conn.execute("SELECT Designation FROM RHS") - - elif table_name == "SHS": - cursor = conn.execute("SELECT Designation FROM SHS") - - elif table_name == "CHS": - cursor = conn.execute("SELECT Designation FROM CHS") - - else: - cursor = conn.execute("SELECT Designation FROM Columns") - rows = cursor.fetchall() - - for row in rows: - lst.append(row) - - final_lst = tuple_to_str(lst,call_type,table_name) - if table_name == "Material" and call_type == "dropdown": - final_lst.append("Custom") - - return final_lst - - -def connect_for_red(table_name): - - """ - Function to fetch designation values from various Tables where source is IS808_Old - """ - - # @author: Arsil - conn = sqlite3.connect(PATH_TO_DATABASE) - lst = [] - if table_name == "Angles": - cursor = conn.execute("SELECT Designation FROM Angles WHERE Source = 'IS808_Old'") - - elif table_name == "Channels": - cursor = conn.execute("SELECT Designation FROM Channels WHERE Source = 'IS808_Old'") - - elif table_name == "Beams": - cursor = conn.execute("SELECT Designation FROM Beams WHERE Source = 'IS808_Old'") - - elif table_name == "Columns": - cursor = conn.execute("SELECT Designation FROM Columns WHERE Source = 'IS808_Old'") - - else: - return [] - rows = cursor.fetchall() - - for row in rows: - lst.append(row) - - final_lst = tuple_to_str_red(lst) - return final_lst - - -def red_list_function(): - - """ - Function to form a list for old values from Columns and Beams table. - """ - - # @author: Arsil - - red_list = [] - red_list_columns = connect_for_red("Columns") - red_list_beams = connect_for_red("Beams") - red_list.extend(red_list_beams) - red_list.extend(red_list_columns) - return red_list - - -def tuple_to_str_popup(tl): - - # @author: Amir - - arr = [] - for v in tl: - val = ''.join(v) - arr.append(val) - return arr - -def tuple_to_str(tl, call_type,table_name=None): - - if call_type is "dropdown" and table_name != 'Material' and table_name != 'Bolt': - arr = ['Select Section'] - else: - arr = [] - for v in tl: - val = ''.join(v) - arr.append(val) - return arr - - -def tuple_to_str_red(tl): - arr = [] - for v in tl: - val = ''.join(v) - arr.append(val) - return arr - -def get_db_header(table_name): - - conn = sqlite3.connect(PATH_TO_DATABASE) - - if table_name == "Angles": - cursor = conn.execute("SELECT * FROM Angles") - - elif table_name == "Channels": - cursor = conn.execute("SELECT * FROM Channels") - - elif table_name == "Beams": - cursor = conn.execute("SELECT * FROM Beams") - - else: - cursor = conn.execute("SELECT * FROM Columns") - - header = [description[0] for description in cursor.description] - - return header - -def get_source(table_name, designation): - - conn = sqlite3.connect(PATH_TO_DATABASE) - - if table_name == "Angles": - cursor = conn.execute("SELECT Source FROM Angles WHERE Designation = ?", (designation,)) - - elif table_name == "Channels": - cursor = conn.execute("SELECT Source FROM Channels WHERE Designation = ?", (designation,)) - - elif table_name == "Beams": - cursor = conn.execute("SELECT Source FROM Beams WHERE Designation = ?", (designation,)) - - else: - cursor = conn.execute("SELECT Source FROM Columns WHERE Designation = ?", (designation,)) - - source = cursor.fetchone()[0] - return str(source) - - -class MaterialValidator(object): - def __init__(self, material): - self.material = str(material) - self.typ = "Unknown" - self.fy_20 = 0 - self.fy_20_40 = 0 - self.fy_40 = 0 - self.fu = 0 - self.custom_format_flag = False - self.invalid_value = "" - self.notations = ["Fy_20", "Fy_20_40", "Fy_40", "Fu"] - material = self.material.split("_") - if len(material) == 5: - self.typ = material[0] - self.fy_20 = material[1] - self.fy_20_40 = material[2] - self.fy_40 = material[3] - self.fu = material[4] - self.values = [self.fy_20, self.fy_20_40, self.fy_40, self.fu] - if self.typ == "Cus": - for i in self.values: - if str(i) != "" and str(i).isdigit(): - self.custom_format_flag = True - else: - self.custom_format_flag = False - break - - def is_already_in_db(self): - if self.material in connectdb("Material", call_type="popup"): - return True - else: - return False - - def is_format_custom(self): - return self.custom_format_flag - - def is_valid_custom(self): - - min_allowed = [165, 165, 165, 165] - max_allowed = [1500, 1500, 1500, 1500] - for i in range(4): - if self.values[i] == "": - continue - if min_allowed[i] <= int(self.values[i]) <= max_allowed[i]: - pass - else: - self.invalid_value = self.notations[i] - break - - if self.invalid_value: - return False - else: - return self.custom_format_flag - -########################## -# Type Keys (Type of input field, tab type etc.) -########################### -TYPE_COMBOBOX = 'ComboBox' -TYPE_COMBOBOX_FREEZE = 'Disable_ComboBoc' -TYPE_TEXTBOX = 'TextBox' -TYPE_TITLE = 'Title' -TYPE_LABEL = 'Label' -TYPE_IMAGE = 'Image' -TYPE_IMAGE_COMPRESSION = 'Image_compression' -TYPE_COMBOBOX_CUSTOMIZED = 'ComboBox_Customized' -TYPE_OUT_BUTTON = 'Output_dock_Button' -TYPE_OUT_DOCK = 'Output_dock_Item' -TYPE_OUT_LABEL = 'Output_dock_Label' -TYPE_BREAK = 'Break' -TYPE_ENTER = 'Enter' -TYPE_TEXT_BROWSER = 'TextBrowser' -TYPE_NOTE = 'Note' -TYPE_WARNING = 'Warning' -DESIGN_FLAG = 'False' -VALUE_NOT_APPLICABLE = 'N/A' -TYPE_TAB_1 = "TYPE_TAB_1" -TYPE_TAB_2 = "TYPE_TAB_2" -TYPE_TAB_3 = "TYPE_TAB_3" -TYPE_SECTION = 'Popup_Section' -TYPE_CUSTOM_MATERIAL = 'New_Material_Popup' -TYPE_CUSTOM_SECTION = 'New_Section_Popup' -TYPE_ENABLE_DISABLE = 'Enable/Disable' -TYPE_CHANGE_TAB_NAME = 'Change tab_name' -TYPE_REMOVE_TAB = 'Remove tab' -TYPE_OVERWRITE_VALIDATION = 'Overwrite_validation' -KEY_IMAGE = 'Image' -TYP_BEARING = "Bearing Bolt" -TYP_FRICTION_GRIP = "Friction Grip Bolt" - -################################### -# Module Keys DONOT CHANGE THESE -################################### -KEY_MAIN_MODULE = 'Main Module' -KEY_MODULE_STATUS = 'Module.Status' - -TYPE_MODULE = 'Window Title' - -KEY_DISP_FINPLATE = 'Fin Plate Connection' -KEY_DISP_ENDPLATE = 'End Plate Connection' -KEY_DISP_CLEATANGLE = 'Cleat Angle Connection' -KEY_DISP_SEATED_ANGLE = 'Seated Angle Connection' -KEY_DISP_BASE_PLATE = 'Base Plate Connection' - -KEY_DISP_BEAMCOVERPLATE = 'Beam-to-Beam Cover Plate Bolted Connection' -KEY_DISP_COLUMNCOVERPLATE = 'Column-to-Column Cover Plate Bolted Connection' -KEY_DISP_BEAMCOVERPLATEWELD = 'Beam-to-Beam Cover Plate Welded Connection' -KEY_DISP_COLUMNCOVERPLATEWELD = 'Column-to-Column Cover Plate Welded Connection' -# KEY_DISP_BEAMENDPLATE = 'Beam End Plate Connection' -KEY_DISP_COLUMNENDPLATE = 'Column-to-Column End Plate Connection' -KEY_DISP_BCENDPLATE = 'Beam-to-Column End Plate Connection' -KEY_DISP_TENSION_BOLTED = 'Tension Member Design - Bolted to End Gusset' -KEY_DISP_TENSION_WELDED = 'Tension Member Design - Welded to End Gusset' -KEY_DISP_COMPRESSION = 'Compression Member' -KEY_DISP_BB_EP_SPLICE = 'Beam-to-Beam End Plate Connection' - -DISP_TITLE_CM = 'Connecting Members' - -################################### -# All Input Keys -################################### -KEY_MODULE = 'Module' -KEY_CONN = 'Connectivity' -KEY_LOCATION = 'Conn_Location' -KEY_ENDPLATE_TYPE = 'EndPlateType' -KEY_MATERIAL = 'Material' -KEY_MATERIAL_ST_SK = 'Material' -KEY_MATERIAL_FU = 'Material.Fu' -KEY_MATERIAL_FY = 'Material.Fy' - - -KEY_SEC_MATERIAL = 'Member.Material' -KEY_SEC_FU = 'Member.Fu' #Extra Keys -KEY_SEC_FY = 'Member.Fy' #Extra Keys - -KEY_SECSIZE = 'Member.Designation' -KEY_SECSIZE_DP = 'Member.Designation_dp' -KEY_SECSIZE_SELECTED = 'Member.Designation_Selected' #Extra Keys for Display -KEY_SUPTNGSEC = 'Member.Supporting_Section.Designation' -KEY_SUPTNGSEC_MATERIAL = 'Member.Supporting_Section.Material' -KEY_A = 'Member.A' -KEY_B = 'Member.B' - - -KEY_SUPTDSEC_FU = 'Member.Supported_Section.Fu' #Extra Keys for DP Display -KEY_SUPTDSEC_FY = 'Member.Supported_Section.Fy' #Extra Keys for DP Display - -KEY_SUPTDSEC = 'Member.Supported_Section.Designation' -KEY_SUPTDSEC_MATERIAL = 'Member.Supported_Section.Material' -KEY_SUPTNGSEC_FU = 'Member.Supporting_Section.Fu' #Extra Keys for DP Display -KEY_SUPTNGSEC_FY = 'Member.Supporting.Section.Fy' #Extra Keys for DP Display - -KEY_LENGTH = 'Member.Length' -KEY_SEC_PROFILE = 'Member.Profile' - -KEY_SHEAR = 'Load.Shear' -KEY_AXIAL = 'Load.Axial' -KEY_MOMENT = 'Load.Moment' - -KEY_D = 'Bolt.Diameter' -KEY_TYP = 'Bolt.Type' -KEY_GRD = 'Bolt.Grade' - -# KEY_DP_BOLT_MATERIAL_G_O = 'Bolt.Material_Grade_OverWrite' -KEY_DP_BOLT_HOLE_TYPE = 'Bolt.Bolt_Hole_Type' -KEY_DP_BOLT_TYPE = 'Bolt.TensionType' -KEY_DP_BOLT_SLIP_FACTOR = 'Bolt.Slip_Factor' - -KEY_CONNECTOR_MATERIAL = 'Connector.Material' -KEY_CONNECTOR_FU = 'Connector.Fu' #Extra Keys for DP Display -KEY_CONNECTOR_FY = 'Connector.Fy' #Extra Keys for DP Display -KEY_CONNECTOR_FY_20 = 'Connector.Fy_20' #Extra Keys for DP Display -KEY_CONNECTOR_FY_20_40 = 'Connector.Fy_20_40' #Extra Keys for DP Display -KEY_CONNECTOR_FY_40 = 'Connector.Fy_40' #Extra Keys for DP Display - -KEY_PLATETHK = 'Connector.Plate.Thickness_List' -KEY_FLANGEPLATE_PREFERENCES = 'Connector.Flange_Plate.Preferences' -KEY_FLANGEPLATE_THICKNESS = 'Connector.Flange_Plate.Thickness_list' -KEY_WEBPLATE_THICKNESS = 'Connector.Web_Plate.Thickness_List' -KEY_ANGLE_LIST='Connector.Angle_List' -KEY_ANGLE_SELECTED = 'Connector.Angle_Selected' -KEY_SEATEDANGLE = 'Connector.Seated_Angle_List' -KEY_TOPANGLE = 'Connector.Top_Angle' -KEY_DISP_ANGLE_LIST = 'Seated Angle List' -KEY_DISP_CLEAT_ANGLE_LIST = 'Cleat Angle List' -KEY_DISP_TOPANGLE_LIST = 'Top Angle List' - -KEY_MOMENT_MAJOR = 'Load.Moment.Major' -KEY_MOMENT_MINOR = 'Load.Moment.Minor' -KEY_ANCHOR_OCF = 'Anchor Bolt.OCF' -KEY_DISP_ANCHOR_OCF = 'Anchor Bolt Outside Column Flange' -KEY_ANCHOR_ICF = 'Anchor Bolt.ICF' -KEY_DISP_ANCHOR_ICF = 'Anchor Bolt Inside Column Flange' -KEY_DISP_ANCHOR_GENERAL = 'General' -KEY_DIA_ANCHOR_OCF = 'Anchor Bolt.OCF.Diameter' -KEY_DIA_ANCHOR_ICF = 'Anchor Bolt.ICF.Diameter' -KEY_TYP_ANCHOR = 'Anchor Bolt.Type' -KEY_GRD_ANCHOR_OCF = 'Anchor Bolt.OCF.Grade' -KEY_GRD_ANCHOR_ICF = 'Anchor Bolt.ICF.Grade' -KEY_GRD_FOOTING = 'Footing.Grade' - - -KEY_DP_WELD_FAB = 'Weld.Fab' -KEY_DP_WELD_MATERIAL_G_O = 'Weld.Material_Grade_OverWrite' -KEY_DP_WELD_TYPE = 'Weld.Type' - -KEY_DP_DETAILING_EDGE_TYPE = 'Detailing.Edge_type' -KEY_DP_DETAILING_GAP = 'Detailing.Gap' -KEY_DP_DETAILING_CORROSIVE_INFLUENCES = 'Detailing.Corrosive_Influences' - -KEY_DP_DESIGN_METHOD = 'Design.Design_Method' - -################### -# Value Keys -################### - -RED_LIST = [KEY_SUPTNGSEC, KEY_SUPTDSEC, KEY_SECSIZE] -VALUES_CONN_SPLICE = ['Coplanar Tension-Compression Flange', 'Coplanar Tension Flange', 'Coplanar Compression Flange'] -CONN_CFBW = 'Column Flange-Beam Web' -CONN_CWBW = 'Column Web-Beam Web' -VALUES_CONN_1 = [CONN_CFBW, CONN_CWBW] -VALUES_CONN_2 = ['Beam-Beam'] -VALUES_CONN_3 = ['Flush End Plate', 'Extended Both Ways'] -VALUES_CONN = VALUES_CONN_1 + VALUES_CONN_2 -VALUES_ENDPLATE_TYPE = ['Flushed - Reversible Moment', 'Extended One Way - Irreversible Moment', 'Extended Both Ways - Reversible Moment'] -# VALUES_CONN_BP = ['Welded Column Base', 'Welded+Bolted Column Base', 'Moment Base Plate', 'Hollow/Tubular Column Base'] -VALUES_CONN_BP = ['Welded Column Base', 'Moment Base Plate', 'Hollow/Tubular Column Base'] -VALUES_LOCATION = ['Select Location','Long Leg', 'Short Leg', 'Web'] - -# TODO: Every one is requested to use VALUES_ALL_CUSTOMIZED key instead of all other keys -VALUES_ALL_CUSTOMIZED = ['All', 'Customized'] -VALUES_ENDPLATE_THICKNESS = ['All', 'Customized'] -VALUES_DIA_ANCHOR = ['All', 'Customized'] -VALUES_GRD_ANCHOR = ['All', 'Customized'] -VALUES_D = ['All', 'Customized'] -VALUES_GRD = ['All', 'Customized'] -VALUES_PLATETHK = ['All', 'Customized'] -VALUES_FLANGEPLATE_THICKNESS = ['All', 'Customized'] -VALUES_WEBPLATE_THICKNESS = ['All', 'Customized'] -VALUES_ANGLESEC= ['All', 'Customized'] - -ALL_WELD_SIZES = [3, 4, 5, 6, 8, 10, 12, 14, 16] -VALUES_TYP_ANCHOR = ['End Plate Type', 'IS 5624-Type A', 'IS 5624-Type B'] -VALUES_GRD_FOOTING = ['Select Grade', 'M10', 'M15', 'M20', 'M25', 'M30', 'M35', 'M40', 'M45', 'M50', 'M55'] -VALUES_TYP = [TYP_BEARING, TYP_FRICTION_GRIP] -TYP_FRICTION_GRIP = 'Friction Grip Bolt' -TYP_BEARING = 'Bearing Bolt' - -# VALUES_GRD_CUSTOMIZED = ['3.6', '4.6', '4.8', '5.6', '5.8', '6.8', '8.8', '9.8', '10.9', '12.9'] -VALUES_GRD_CUSTOMIZED = IS1367_Part3_2002.get_bolt_PC() - -# standard as per IS 1730:1989 -PLATE_THICKNESS_IS_1730_1989 = ['5', '6', '7', '8', '10', '12', '14', '16', '18', '20', '22', '25', '28', '32', '36', '40', '45', '50', '56', '63'] -# standard as per SAIL's product brochure -PLATE_THICKNESS_SAIL = ['8', '10', '12', '14', '16', '18', '20', '22', '25', '28', '32', '36', '40', '45', '50', '56', '63', '75', '80', '90', '100', - '110', '120'] - -VALUES_PLATETHICKNESS_CUSTOMIZED = PLATE_THICKNESS_SAIL -VALUES_PLATETHK_CUSTOMIZED = PLATE_THICKNESS_SAIL -VALUES_ENDPLATE_THICKNESS_CUSTOMIZED = PLATE_THICKNESS_SAIL -VALUES_COLUMN_ENDPLATE_THICKNESS_CUSTOMIZED = PLATE_THICKNESS_SAIL - -# TODO: delete the below list (commented) after verification -# VALUES_PLATETHK_CUSTOMIZED = ['3', '4', '5', '6', '8', '10', '12', '14', '16', '18', '20', '22', '24','25', '26', '28', '30','32','36','40','45','50','56','63','80'] -# VALUES_ENDPLATE_THICKNESS_CUSTOMIZED = ['3', '4', '5', '6', '8', '10', '12', '14', '16', '18', '20', '22', '24', '26', '28', '30'] -# VALUES_COLUMN_ENDPLATE_THICKNESS_CUSTOMIZED = VALUES_ENDPLATE_THICKNESS_CUSTOMIZED[3:12] + ['25','28','32','36','40','45','50','56','63','80'] - - - -VALUES_FLANGEPLATE_PREFERENCES = ['Outside','Outside + Inside'] -VALUES_LOCATION_1 = ['Long Leg', 'Short Leg'] -VALUES_LOCATION_2 = ["Web"] -VALUES_SECTYPE = ['Select Type','Beams','Columns','Angles','Back to Back Angles','Star Angles','Channels','Back to back Channels'] - -VALUES_CONNLOC_BOLT = ['Bolted','Web','Flange','Leg','Back to Back Web','Back to Back Angles','Star Angles'] -VALUES_CONNLOC_WELD = ['Welded','Web','Flange','Leg','Back to Back Web','Back to Back Angles','Star Angles'] -VALUES_DIAM = connectdb("Bolt") -# VALUES_DIAM = ['Select diameter','12','16','20','24','30','36'] -VALUES_IMG_TENSIONBOLTED = ["ResourceFiles/images/bA.png","ResourceFiles/images/bBBA.png","ResourceFiles/images/bSA.png","ResourceFiles/images/bC.png","ResourceFiles/images/bBBC.png"] -VALUES_IMG_TENSIONWELDED = ["ResourceFiles/images/wA.png","ResourceFiles/images/wBBA.png","ResourceFiles/images/wSA.png","ResourceFiles/images/wC.png","ResourceFiles/images/wBBC.png"] -VALUES_IMG_TENSIONBOLTED_DF01 = ["ResourceFiles/images/equaldp.png","ResourceFiles/images/bblequaldp.png","ResourceFiles/images/bbsequaldp.png","ResourceFiles/images/salequaldp.png","ResourceFiles/images/sasequaldp.png"] -VALUES_IMG_TENSIONBOLTED_DF02 = ["ResourceFiles/images/unequaldp.png","ResourceFiles/images/bblunequaldp.png","ResourceFiles/images/bbsunequaldp.png","ResourceFiles/images/salunequaldp.png","ResourceFiles/images/sasunequaldp.png"] - -VALUES_IMG_TENSIONBOLTED_DF03 = ["ResourceFiles/images/Slope_Channel.png","ResourceFiles/images/Parallel_Channel.png","ResourceFiles/images/Slope_BBChannel.png","ResourceFiles/images/Parallel_BBChannel.png"] - -VALUES_IMG_BEAM = ["ResourceFiles/images/Slope_Beam.png","ResourceFiles/images/Parallel_Beam.png"] -VALUES_IMG_HOLLOWSECTION = ["ResourceFiles/images/SHS.png","ResourceFiles/images/RHS.png","ResourceFiles/images/CHS.png"] - -VALUES_BEAMSEC = connectdb("Beams") -VALUES_SECBM = connectdb("Beams") -VALUES_COLSEC = connectdb("Columns") -VALUES_MATERIAL = connectdb("Material") -VALUES_MATERIAL_SELECTED = "E 250 (Fe 410 W)A" -VALUES_PRIBM = connectdb("Beams") - - -############################ -# Display Keys (Input Dock, Output Dock, Design preference, Design report) -############################ - -KEY_DISP_SHEAR_YLD = 'Shear Yielding Capacity (kN)' -KEY_DISP_SHEAR_RUP = 'Shear Rupture Capacity (kN)' -KEY_DISP_PLATE_BLK_SHEAR_SHEAR = 'Block Shear Capacity in Shear (kN)' -KEY_DISP_PLATE_BLK_SHEAR_TENSION = 'Block Shear Capacity in Tension (kN)' -KEY_DISP_SHEAR_CAPACITY = 'Shear Capacity (kN)' -KEY_DISP_BEARING_LENGTH = 'Bearing Length' -KEY_DISP_ALLOW_SHEAR = 'Allowable Shear Capacity (kN)' -DISP_LOWSHEAR = 'Limited to low shear capacity' - -KEY_DISP_BLK_SHEAR = 'Block Shear Capacity (kN)' -KEY_DISP_MOM_DEMAND = 'Moment Demand (kNm)' -KEY_DISP_MOM_CAPACITY = 'Moment Capacity (kNm)' -DISP_MIN_PITCH = 'Min. Pitch Distance (mm)' -DISP_MAX_PITCH = 'Max. Pitch Distance (mm)' -DISP_MIN_GAUGE = 'Min. Gauge Distance (mm)' -DISP_MAX_GAUGE = 'Max. Gauge Distance (mm)' -DISP_CS_GAUGE = 'Cross-centre Gauge Distance (mm)' -DISP_MIN_EDGE = 'Min. Edge Distance (mm)' -KEY_SPACING = "Spacing Check" -DISP_MAX_EDGE = 'Max. Edge Distance (mm)' -DISP_MIN_END = 'Min. End Distance (mm)' -DISP_MAX_END = 'Max. End Distance (mm)' -DISP_MIN_PLATE_HEIGHT = 'Min. Plate Height (mm)' -DISP_MAX_PLATE_HEIGHT = 'Max. Plate Height (mm)' -DISP_MIN_PLATE_LENGTH = 'Min. Plate Length (mm)' -DISP_MAX_PLATE_WIDTH = 'Max. Plate Width (mm)' -DISP_MIN_PLATE_WIDTH = 'Min. Plate Width (mm)' -DISP_MIN_LEG_LENGTH = 'Min. Leg Length (mm)' -DISP_MIN_CLEAT_HEIGHT = 'Min. Cleat Angle Height' -DISP_MAX_CLEAT_HEIGHT = 'Max. Cleat Angle Height' -DISP_MIN_CLEAT_THK = 'Min. Cleat Angle Thickness (mm)' -DISP_MIN_WIDTH = 'Minimum Width (mm)' -DISP_MIN_PLATE_THICK = 'Min. Plate Thickness (mm)' - -######### Minimun for Flange#### -DISP_MIN_FLANGE_PLATE_HEIGHT = 'Min. Flange Plate Width (mm)' -DISP_MAX_FLANGE_PLATE_HEIGHT = 'Max. Flange Plate Width (mm)' -DISP_MIN_FLANGE_PLATE_LENGTH = 'Min. Flange Plate Length (mm)' -DISP_MIN_FLANGE_PLATE_THICK = 'Min. Flange Plate Thickness (mm)' - -######### Minimun for Flange#### -DISP_MIN_WEB_PLATE_HEIGHT = 'Min. Web Plate Height (mm)' -DISP_MAX_WEB_PLATE_HEIGHT = 'Max. Web Plate Height (mm)' -DISP_MIN_WEB_PLATE_LENGTH = 'Min. Web Plate Width (mm)' -DISP_MIN_WEB_PLATE_THICK = 'Min. Web Plate Thickness (mm)' - - - - -DISP_MIN_PLATE_INNERHEIGHT = 'Min. Inner Plate Width (mm)' -DISP_MAX_PLATE_INNERHEIGHT = 'Max. Inner Plate Width (mm)' -DISP_MIN_PLATE_INNERLENGTH = 'Min. Inner Plate Length (mm)' - - -KEY_DISP_FU = 'Ultimate Strength, Fu (MPa)' -KEY_DISP_FY = 'Yield Strength, Fy (MPa)' -KEY_DISP_IR = 'Interaction Ratio' -DISP_WELD_SIZE = 'Weld Size (mm)' -DISP_MIN_WELD_SIZE = 'Min. Weld Size (mm)' -DISP_MAX_WELD_SIZE = 'Max. Weld Size (mm)' -DISP_THROAT = 'Throat Thickness (mm)' -DISP_WEB_WELD_SIZE_REQ = 'Web Weld Size Required (mm)' - -DISP_WELD_STRENGTH = 'Weld Strength (N/mm)' -DISP_WELD_STRENGTH_MPA = 'Weld Strength (N/mm2)' -KEY_DISP_FY_20 = 'Yield Strength, Fy (MPa) (0-20mm)' -KEY_DISP_FY_20_40 = 'Yield Strength, Fy (MPa) (20-40mm)' -KEY_DISP_FY_40 = 'Yield Strength, Fy (MPa) (>40mm)' -DISP_TITLE_ANCHOR_BOLT = 'Anchor Bolt' -DISP_TITLE_ANCHOR_BOLT_OUTSIDE_CF = 'Anchor Bolt - Outside Column Flange' -DISP_TITLE_ANCHOR_BOLT = 'Anchor Bolt' -DISP_TITLE_FOOTING = 'Pedestal/Footing' - -KEY_DISP_CONN = 'Connectivity' - -KEY_DISP_ENDPLATE_TYPE = 'End Plate Type' - - -# VALUES_CONN_BP = ['Welded-Slab Base', 'Bolted-Slab Base', 'Gusseted Base Plate', 'Hollow Section'] - -KEY_DISP_LENGTH = 'Length (mm) *' -KEY_DISP_LOCATION = 'Conn_Location *' -KEY_DISP_MATERIAL = 'Material' -KEY_DISP_SUPTNGSEC = 'Supporting Section' -KEY_DISP_SUPTNGSEC_REPORT = 'Supporting Section - Mechanical Properties' -KEY_DISP_COLSEC = 'Column Section *' -KEY_DISP_COLSEC_REPORT = 'Column Section' -KEY_DISP_PRIBM = 'Primary Beam *' -KEY_DISP_SUPTDSEC = 'Supported Section' -KEY_DISP_SUPTDSEC_REPORT = 'Supported Section - Mechanical Properties' -KEY_DISP_BEAMSEC = 'Beam Section *' -KEY_DISP_BEAMSEC_REPORT = 'Beam Section' -KEY_DISP_SECBM = 'Secondary Beam *' -DISP_TITLE_FSL = 'Factored Loads' -KEY_DISP_MOMENT = 'Bending Moment (kNm)' - -KEY_DISP_TOP_ANGLE = 'Top Angle' - -KEY_DISP_DIA_ANCHOR = 'Diameter(mm)' -DISP_TITLE_BOLT = 'Bolt' -DISP_TITLE_CRITICAL_BOLT = 'Critical Bolt Design' -DISP_TITLE_CRITICAL_BOLT_SHEAR = 'Critical Bolt - Shear Design' -DISP_TITLE_BOLT_CAPACITY = 'Bolt Capacity' - -DISP_TITLE_FLANGESPLICEPLATE = 'Flange Splice Plate ' -DISP_TITLE_FLANGESPLICEPLATE_OUTER = 'Outer Plate ' -DISP_TITLE_FLANGESPLICEPLATE_INNER = 'Inner Plate ' -KEY_DISP_SLENDER = 'Slenderness' - - -KEY_DISP_PLATETHK = 'Thickness (mm)' -KEY_DISP_DPPLATETHK = 'Endplate thickness, T (mm)' -KEY_DISP_DPPLATETHK01 = 'Endplate thickness, Tp (mm)' - -DISP_TITLE_TENSION = 'Tension Capacity' -KEY_DISP_FLANGESPLATE_PREFERENCES = 'Preference' -KEY_DISP_FLANGESPLATE_THICKNESS = 'Thickness (mm)' -KEY_DISP_INNERFLANGESPLATE_THICKNESS = 'Thickness (mm)' - -DISP_TITLE_WELD = 'Weld' -DISP_TITLE_WELD_CAPACITY = 'Weld Capacity' -DISP_TITLE_END_CONNECTION = 'End Connection' -DISP_TITLE_WELD_DETAILS = 'Weld Details' -DISP_TITLE_CONN_DETAILS = 'Connection Details' - - -KEY_DISP_FLANGE_CAPACITY= 'Capacity' -KEY_DISP_FLANGE_PLATE_GAUGE ="Gauge Distance (mm)" -KEY_DISP_FLANGE_SPACING = 'Spacing (mm)' -KEY_DISP_END_DIST_FLANGE = 'End Distance' -KEY_DISP_EDGEDIST_FLANGE= 'Edge Distance (mm)' -KEY_DISP_FLANGE_PLATE_PITCH = 'Pitch Distance (mm)' - -KEY_DISP_FLANGE_PLATE_TEN_CAP ="Flange Plate Tension Capacity (kN)" -DISP_TITLE_SECTION = 'Section Details' -DISP_TITLE_TENSION_SECTION = 'Section Details' -SECTION_CLASSIFICATION = "Section Classification" - -KEY_DISP_D = 'Diameter (mm)' -KEY_DISP_SHEAR = 'Shear Force (kN)' -KEY_DISP_AXIAL = 'Axial Force (kN)' -KEY_DISP_AXIAL_STAR = 'Axial (kN)* ' -DISP_TITLE_PLATE = 'Plate' -KEY_DISP_TYP = 'Type' -KEY_DISP_TYP_ANCHOR = 'Anchor Type' -KEY_DISP_GRD_ANCHOR = 'Property Class' -KEY_DISP_GRD_FOOTING = 'Grade*' -KEY_DISP_GRD = 'Property Class' -KEY_DISP_BOLT_PRE_TENSIONING = 'Bolt Tension' - -KEY_DISP_MOMENT_MAJOR = ' - Major axis (Mz-z)' -KEY_DISP_MOMENT_MINOR = ' - Minor axis (My-y)' - -# Applied load -KEY_INTERACTION_RATIO ="Interaction Ratio" -MIN_LOADS_REQUIRED ="Minimum Required Load" -KEY_DISP_APPLIED_SHEAR_LOAD = 'Applied Shear Force (kN)' -KEY_DISP_APPLIED_AXIAL_FORCE = 'Applied Axial Force (kN)' -KEY_DISP_APPLIED_MOMENT_LOAD = 'Applied Moment (kNm)' -KEY_DISP_AXIAL_FORCE_CON = 'Axial Load Considered (kN)' - -# capacity - -KEY_OUT_DISP_AXIAL_CAPACITY = "Axial Capacity Member (kN)" -KEY_OUT_DISP_SHEAR_CAPACITY = "Shear Capacity Member (kN)" -KEY_OUT_DISP_MOMENT_CAPACITY = "Moment Capacity Member (kNm)" -KEY_OUT_DISP_PLASTIC_MOMENT_CAPACITY = 'Plastic Moment Capacity (kNm)' -KEY_OUT_DISP_MOMENT_D_DEFORMATION= 'Moment Deformation Criteria (kNm)' -KEY_OUT_DISP_SHEAR_CAPACITY_M = "Shear Capacity (kN)" - - -KEY_OUT_DIA_ANCHOR = 'Anchor Bolt.Diameter' -KEY_DISP_OUT_DIA_ANCHOR = 'Diameter (mm)' -KEY_OUT_GRD_ANCHOR = 'Anchor Bolt.Grade' -KEY_DISP_OUT_GRD_ANCHOR = 'Property Class' -KEY_OUT_ANCHOR_BOLT_LENGTH = 'Anchor Bolt.Length' -KEY_DISP_OUT_ANCHOR_BOLT_LENGTH = 'Anchor Length (mm)' -KEY_OUT_ANCHOR_BOLT_NO = 'Anchor Bolt.No of Anchor Bolts' -KEY_DISP_OUT_ANCHOR_BOLT_NO = 'No. of Anchors' - - -KEY_OUT_DISP_ANCHOR_BOLT_SHEAR = 'Shear Capacity (kN)' -KEY_OUT_DISP_ANCHOR_BOLT_BEARING = 'Bearing Capacity (kN)' -KEY_OUT_DISP_ANCHOR_BOLT_CAPACITY = 'Bolt Capacity (kN)' -KEY_OUT_DISP_ANCHOR_BOLT_COMBINED = 'Combined Capacity (kN)' -KEY_OUT_DISP_ANCHOR_BOLT_TENSION_DEMAND = 'Tension Demand (kN)' -KEY_OUT_DISP_ANCHOR_BOLT_TENSION = 'Tension Capacity (kN)' - - -DISP_TITLE_ANCHOR_BOLT_UPLIFT = 'Anchor Bolt - Inside Column Flange' -KEY_OUT_DIA_ANCHOR_UPLIFT = 'Anchor Bolt.Diameter_Uplift' -KEY_DISP_OUT_DIA_ANCHOR_UPLIFT = 'Diameter (mm)' -KEY_OUT_GRD_ANCHOR_UPLIFT = 'Anchor Bolt.Grade_Uplift' -KEY_DISP_OUT_GRD_ANCHOR_UPLIFT = 'Property Class' -KEY_OUT_ANCHOR_UPLIFT_BOLT_NO = 'Anchor Bolt.No of Anchor Bolts_Uplift' -KEY_DISP_OUT_ANCHOR_UPLIFT_BOLT_NO = 'No. of Anchor Bolts' -KEY_OUT_ANCHOR_BOLT_LENGTH_UPLIFT = 'Anchor Bolt.Length_Uplift' -KEY_DISP_OUT_ANCHOR_BOLT_LENGTH_UPLIFT = 'Anchor Length (mm)' -KEY_OUT_ANCHOR_BOLT_TENSION_UPLIFT = 'Anchor Bolt.Tension_Uplift' -KEY_OUT_DISP_ANCHOR_BOLT_TENSION_UPLIFT = 'Tension Capacity (kN)' -KEY_OUT_ANCHOR_BOLT_TENSION_DEMAND_UPLIFT = 'Anchor Bolt.Tension_Demand_Uplift' -KEY_OUT_DISP_ANCHOR_BOLT_TENSION_DEMAND_UPLIFT = 'Tension Demand (kN)' - -DISP_TITLE_MEMBER_CAPACITY ="Member Capacity" -KEY_DISP_MEMBER_CAPACITY = "Member Capacity" - - -KEY_OUT_DISP_BASEPLATE_WIDTH = 'Width (mm)' -KEY_OUT_DISP_BASEPLATE_LENGTH = 'Length (mm)' -KEY_OUT_DISP_BASEPLATE_THICKNNESS = 'Thickness (mm)' -DISP_TITLE_DETAILING = 'Detailing' -DISP_TITLE_TYPICAL_DETAILING = 'Typical Detailing' -DISP_TITLE_DETAILING_OCF = 'Detailing - Outside Column Flange' -DISP_TITLE_DETAILING_ICF = 'Detailing - Inside Column Flange' - -KEY_OUT_DISP_DETAILING_NO_OF_ANCHOR_BOLT = 'Total No. of Anchor Bolts' - -KEY_OUT_DISP_DETAILING_PITCH_DISTANCE = 'Pitch Distance (mm)' -KEY_IN_DISP_DETAILING_PITCH_DISTANCE = 'Pitch Distance (mm)' - -KEY_OUT_DISP_DETAILING_GAUGE_DISTANCE = 'Gauge Distance (mm)' -KEY_IN_DISP_DETAILING_GAUGE_DISTANCE = 'Gauge Distance (mm)' -KEY_OUT_DISP_DETAILING_CS_GAUGE_DISTANCE = 'Cross-centre Gauge (mm)' -KEY_OUT_DETAILING_END_DISTANCE = 'Detailing.EndDistanceOut' -KEY_IN_DETAILING_END_DISTANCE = 'Detailing.EndDistanceIn' - -KEY_OUT_DISP_DETAILING_END_DISTANCE = 'End Distance (mm)' -KEY_IN_DISP_DETAILING_END_DISTANCE = 'End Distance (mm)' - -KEY_OUT_DISP_DETAILING_EDGE_DISTANCE = "Edge Distance (mm)" -KEY_IN_DISP_DETAILING_EDGE_DISTANCE = "Edge Distance (mm)" - -KEY_OUT_DISP_DETAILING_PROJECTION = 'Effective Projection (mm)' -DISP_TITLE_STIFFENER_PLATE = 'Stiffener Plate' -DISP_OUT_TITLE_STIFFENER_PLATE = 'Stiffener.StiffenerPlate' -DISP_OUT_TITLE_CHS_STIFFENER_PLATE = 'Stiffener.StiffenerPlate' -DISP_TITLE_CONTINUITY_PLATE = 'Continuity Plate' -DISP_TITLE_COL_WEB_STIFFENER_PLATE = 'Column Web Stiffener Plate' -KEY_OUT_DISP_STIFFENER_PLATE_THICKNESS = 'Thickness (mm)' -KEY_OUT_DISP_STIFFENER_PLATE_SHEAR_DEMAND = 'Shear Demand (kN)' -KEY_OUT_DISP_STIFFENER_PLATE_SHEAR = 'Shear Capacity (kN)' -KEY_OUT_DISP_STIFFENER_PLATE_MOMENT_DEMAND = 'Moment Demand (kNm)' -KEY_OUT_DISP_STIFFENER_PLATE_MOMENT = 'Moment Capacity (kNm)' -KEY_OUT_DISP_GUSSET_PLATE_MOMENT = 'Moment Capacity (kNm)' -KEY_OUT_DISP_GUSSET_PLATE_MOMENT_DEMAND = 'Moment Demand (kNm)' -KEY_OUT_DISP_GUSSET_PLATE_SHEAR = 'Shear Capacity (kN)' -KEY_OUT_DISP_GUSSET_PLATE_THICKNESS = 'Thickness (mm)' -KEY_OUT_DISP_GUSSET_PLATE_SHEAR_DEMAND = 'Shear Demand (kN)' -DISP_TITLE_GUSSET_PLATE = 'Gusset Plate Details' -KEY_DISP_FLANGE_PLATE_LENGTH = 'Length (mm)' -KEY_DISP_FLANGE_PLATE_HEIGHT = 'Width (mm)' -KEY_DISP_INNERFLANGESPLICEPLATE = "Inner Plate Details" -DISP_TITLE_INNERFLANGESPLICEPLATE = 'Inner Flange splice plate' -KEY_DISP_INNERFLANGE_PLATE_HEIGHT = 'Width (mm)' -KEY_DISP_INNERFLANGE_PLATE_LENGTH = 'Length (mm)' - - - - -# DISP_TITLE_GUSSET_PLATE = 'Gusset Plate' -# KEY_OUT_GUSSET_PLATE_THICKNNESS = 'GussetPlate.Thickness' -# KEY_OUT_DISP_GUSSET_PLATE_THICKNESS = 'Thickness (mm)' -# KEY_OUT_GUSSET_PLATE_SHEAR_DEMAND = 'GussetPlate.Shear_Demand' -# KEY_OUT_DISP_GUSSET_PLATE_SHEAR_DEMAND = 'Shear Demand (kN)' -# KEY_OUT_GUSSET_PLATE_SHEAR = 'GussetPlate.Shear' -# KEY_OUT_DISP_GUSSET_PLATE_SHEAR = 'Shear Capacity (kN)' -# KEY_OUT_GUSSET_PLATE_MOMENT_DEMAND = 'GussetPlate.Moment_Demand' -# KEY_OUT_DISP_GUSSET_PLATE_MOMENT_DEMAND = 'Moment Demand (kNm)' -# KEY_OUT_GUSSET_PLATE_MOMENT = 'GussetPlate.Moment' -# KEY_OUT_DISP_GUSSET_PLATE_MOMENT = 'Moment Capacity (kNm)' - -KEY_OUT_STIFFENER_PLATE_FLANGE = 'Stiffener_Plate.Column_flange' -KEY_DISP_OUT_STIFFENER_PLATE_FLANGE = 'Stiffener Plate' -DISP_TITLE_STIFFENER_PLATE_FLANGE = 'Stiffener Plate along Column flange' -KEY_OUT_STIFFENER_PLATE_FLANGE_LENGTH = 'Stiffener_Plate_Flange.Length' -KEY_OUT_DISP_STIFFENER_PLATE_FLANGE_LENGTH = 'Length (mm)' -KEY_OUT_STIFFENER_PLATE_FLANGE_HEIGHT = 'Stiffener_Plate_Flange.Height' -KEY_OUT_DISP_STIFFENER_PLATE_FLANGE_HEIGHT = 'Height (mm)' -KEY_OUT_STIFFENER_PLATE_FLANGE_THICKNNESS = 'Stiffener_Plate_Flange.Thickness' -KEY_OUT_DISP_STIFFENER_PLATE_FLANGE_THICKNESS = 'Thickness (mm)' -KEY_OUT_STIFFENER_PLATE_FLANGE_SHEAR_DEMAND = 'Stiffener_Plate_Flange.Shear_Demand' -KEY_OUT_DISP_STIFFENER_PLATE_FLANGE_SHEAR_DEMAND = 'Shear Demand (kN)' -KEY_OUT_STIFFENER_PLATE_FLANGE_SHEAR = 'Stiffener_Plate_Flange.Shear' -KEY_OUT_DISP_STIFFENER_PLATE_FLANGE_SHEAR = 'Shear Capacity (kN)' -KEY_OUT_STIFFENER_PLATE_FLANGE_MOMENT_DEMAND = 'Stiffener_Plate_Flange.Moment_Demand' -KEY_OUT_DISP_STIFFENER_PLATE_FLANGE_MOMENT_DEMAND = 'Moment Demand (kNm)' -KEY_OUT_STIFFENER_PLATE_FLANGE_MOMENT = 'Stiffener_Plate_Flange.Moment' -KEY_OUT_DISP_STIFFENER_PLATE_FLANGE_MOMENT = 'Moment Capacity (kNm)' - -KEY_OUT_STIFFENER_PLATE_ALONG_WEB = 'Stiffener_Plate.Along_Column_web' -KEY_DISP_OUT_STIFFENER_PLATE_ALONG_WEB = 'Stiffener Plate' -DISP_TITLE_STIFFENER_PLATE_ALONG_WEB = 'Stiffener Plate along Column web' -KEY_OUT_STIFFENER_PLATE_ALONG_WEB_LENGTH = 'Stiffener_Plate_along_Web.Length' -KEY_OUT_DISP_STIFFENER_PLATE_ALONG_WEB_LENGTH = 'Length (mm)' -KEY_OUT_STIFFENER_PLATE_ALONG_WEB_HEIGHT = 'Stiffener_Plate_along_Web.Height' -KEY_OUT_DISP_STIFFENER_PLATE_ALONG_WEB_HEIGHT = 'Height (mm)' -KEY_OUT_STIFFENER_PLATE_ALONG_WEB_THICKNNESS = 'Stiffener_Plate_along_Web.Thickness' -KEY_OUT_DISP_STIFFENER_PLATE_ALONG_WEB_THICKNESS = 'Thickness (mm)' -KEY_OUT_STIFFENER_PLATE_ALONG_WEB_SHEAR_DEMAND = 'Stiffener_Plate_along_Web.Shear_Demand' -KEY_OUT_DISP_STIFFENER_PLATE_ALONG_WEB_SHEAR_DEMAND = 'Shear Demand (kN)' -KEY_OUT_STIFFENER_PLATE_ALONG_WEB_SHEAR = 'Stiffener_Plate_along_Web.Shear' -KEY_OUT_DISP_STIFFENER_PLATE_ALONG_WEB_SHEAR = 'Shear Capacity (kN)' -KEY_OUT_STIFFENER_PLATE_ALONG_WEB_MOMENT_DEMAND = 'Stiffener_Plate_along_Web.Moment_Demand' -KEY_OUT_DISP_STIFFENER_PLATE_ALONG_WEB_MOMENT_DEMAND = 'Moment Demand (kNm)' -KEY_OUT_STIFFENER_PLATE_ALONG_WEB_MOMENT = 'Stiffener_Plate_along_Web.Moment' -KEY_OUT_DISP_STIFFENER_PLATE_ALONG_WEB_MOMENT = 'Moment Capacity (kNm)' - -KEY_OUT_STIFFENER_PLATE_ACROSS_WEB = 'Stiffener_Plate.Across_Column_web' -KEY_DISP_OUT_STIFFENER_PLATE_ACROSS_WEB = 'Stiffener Plate' -DISP_TITLE_STIFFENER_PLATE_ACROSS_WEB = 'Stiffener Plate across Column web' -KEY_OUT_STIFFENER_PLATE_ACROSS_WEB_LENGTH = 'Stiffener_Plate_across_Web.Length' -KEY_OUT_DISP_STIFFENER_PLATE_ACROSS_WEB_LENGTH = 'Length (mm)' -KEY_OUT_STIFFENER_PLATE_ACROSS_WEB_HEIGHT = 'Stiffener_Plate_across_Web.Height' -KEY_OUT_DISP_STIFFENER_PLATE_ACROSS_WEB_HEIGHT = 'Height (mm)' -KEY_OUT_STIFFENER_PLATE_ACROSS_WEB_THICKNNESS = 'Stiffener_Plate_across_Web.Thickness' -KEY_OUT_DISP_STIFFENER_PLATE_ACROSS_WEB_THICKNESS = 'Thickness (mm)' -KEY_OUT_STIFFENER_PLATE_ACROSS_WEB_SHEAR_DEMAND = 'Stiffener_Plate_across_Web.Shear_Demand' -KEY_OUT_DISP_STIFFENER_PLATE_ACROSS_WEB_SHEAR_DEMAND = 'Shear Demand (kN)' -KEY_OUT_DISP_STIFFENER_PLATE_SHEAR_DEMAND = 'Shear Demand (kN)' -KEY_OUT_STIFFENER_PLATE_ACROSS_WEB_SHEAR = 'Stiffener_Plate_across_Web.Shear' -KEY_OUT_DISP_STIFFENER_PLATE_ACROSS_WEB_SHEAR = 'Shear Capacity (kN)' -KEY_OUT_STIFFENER_PLATE_ACROSS_WEB_MOMENT_DEMAND = 'Stiffener_Plate_across_Web.Moment_Demand' -KEY_OUT_DISP_STIFFENER_PLATE_ACROSS_WEB_MOMENT_DEMAND = 'Moment Demand (kNm)' -KEY_OUT_STIFFENER_PLATE_ACROSS_WEB_MOMENT = 'Stiffener_Plate_across_Web.Moment' -KEY_OUT_DISP_STIFFENER_PLATE_ACROSS_WEB_MOMENT = 'Moment Capacity (kNm)' - - -KEY_OUT_SHEAR_KEY = 'Shear Key.Along_both_direction' -KEY_OUT_SHEAR_KEY_TYPICAL_DETAILS = 'Shear Key.TypicalDetails' -KEY_DISP_OUT_SHEAR_KEY = 'Shear Key' -KEY_DISP_OUT_SHEAR_KEY_TYPICAL_DETAILS = 'Typical Details' -DISP_TITLE_SHEAR_KEY = 'Shear Design' -KEY_OUT_SHEAR_RESISTANCE = 'ShearDesign.Resistance' -KEY_OUT_DISP_SHEAR_RESISTANCE = 'Shear Resistance (kN)' -KEY_OUT_SHEAR_KEY_REQ = 'Shear_key.Required' -KEY_OUT_DISP_SHEAR_KEY_REQ = 'Key Required?' -KEY_OUT_SHEAR_KEY_LENGTH = 'Shear_key.Length' -KEY_OUT_DISP_SHEAR_KEY_LENGTH = 'Length (mm)' -KEY_OUT_SHEAR_KEY_DEPTH = 'Shear_key.Depth' -KEY_OUT_DISP_SHEAR_KEY_DEPTH = 'Depth (mm)' -KEY_OUT_SHEAR_KEY_THICKNESS = 'Shear_key.Thickness' -KEY_OUT_DISP_SHEAR_KEY_THICKNESS = 'Thickness (mm)' -KEY_OUT_SHEAR_KEY_STRESS = 'Shear_key.Stress' -KEY_OUT_DISP_SHEAR_KEY_STRESS = 'Bearing Stress (N/mm2)' -KEY_OUT_SHEAR_KEY_MOM_DEMAND = 'Shear_key.MomentDemand' -KEY_OUT_DISP_SHEAR_KEY_MOM_DEMAND = 'Moment Demand (kNm)' -KEY_OUT_SHEAR_KEY_MOM_CAPACITY = 'Shear_key.MomentCapacity' -KEY_OUT_DISP_SHEAR_KEY_MOM_CAPACITY = 'Moment Capacity (kNm)' - -# -# DISP_TITLE_STIFFENER_PLATE = 'Stiffener Plate' -# KEY_OUT_STIFFENER_PLATE_THICKNNESS = 'StiffenerPlate.Thickness' -# KEY_OUT_DISP_STIFFENER_PLATE_THICKNESS = 'Thickness (mm)' -# KEY_OUT_STIFFENER_PLATE_SHEAR_DEMAND = 'StiffenerPlate.Shear_Demand' -# KEY_OUT_DISP_STIFFENER_PLATE_SHEAR_DEMAND = 'Shear Demand (kN)' -# KEY_OUT_STIFFENER_PLATE_SHEAR = 'StiffenerPlate.Shear' -# KEY_OUT_DISP_STIFFENER_PLATE_SHEAR = 'Shear Capacity (kN)' -# KEY_OUT_STIFFENER_PLATE_MOMENT_DEMAND = 'StiffenerPlate.Moment_Demand' -# KEY_OUT_DISP_STIFFENER_PLATE_MOMENT_DEMAND = 'Moment Demand (kNm)' -# KEY_OUT_STIFFENER_PLATE_MOMENT = 'StiffenerPlate.Moment' -# KEY_OUT_DISP_STIFFENER_PLATE_MOMENT = 'Moment Capacity (kNm)' - - -KEY_DP_ANCHOR_BOLT_DESIGNATION_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Designation' -KEY_DP_ANCHOR_BOLT_DESIGNATION_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Designation' -KEY_DP_ANCHOR_BOLT_TYPE_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Type' -KEY_DP_ANCHOR_BOLT_TYPE_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Type' -KEY_DISP_DP_ANCHOR_BOLT_TYPE = 'Anchor Bolt Type' -KEY_DP_ANCHOR_BOLT_HOLE_TYPE_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Bolt_Hole_Type' -KEY_DP_ANCHOR_BOLT_HOLE_TYPE_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Bolt_Hole_Type' -KEY_DISP_DP_ANCHOR_BOLT_HOLE_TYPE = 'Anchor Bolt Hole Type' -KEY_DISP_REPORT_HOLE_TYPE = 'Hole Type' -KEY_DP_ANCHOR_BOLT_MATERIAL_G_O_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Material_Grade_OverWrite' -KEY_DP_ANCHOR_BOLT_MATERIAL_G_O_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Material_Grade_OverWrite' -KEY_DISP_DP_ANCHOR_BOLT_MATERIAL_G_O = 'Material Grade, Fu (MPa)' -KEY_DISP_DP_ANCHOR_BOLT_DESIGN_PARA = 'HSFG bolt design parameters:' -KEY_DP_ANCHOR_BOLT_SLIP_FACTOR = 'DesignPreferences.Anchor_Bolt.Slip_Factor' -KEY_DISP_DP_ANCHOR_BOLT_SLIP_FACTOR = 'Slip factor (µ_f)' -KEY_DP_ANCHOR_BOLT_GALVANIZED_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Galvanized' -KEY_DP_ANCHOR_BOLT_GALVANIZED_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Galvanized' -KEY_DISP_DP_ANCHOR_BOLT_GALVANIZED = 'Anchor Bolt Galvanized?' -KEY_DP_ANCHOR_BOLT_LENGTH_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Length' -KEY_DP_ANCHOR_BOLT_LENGTH_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Length' -KEY_DISP_DP_ANCHOR_BOLT_LENGTH = 'Total Length (mm)' -KEY_DP_ANCHOR_BOLT_FRICTION = 'DesignPreferences.Anchor_Bolt.Friction_coefficient' -KEY_DISP_DP_ANCHOR_BOLT_FRICTION = 'Friction Coefficient
(between concrete and anchor bolt)' - - -KEY_DISP_DP_BOLT_TYPE = 'Bolt tensioning type' - -################################### -# Key for Storing Shear sub-key of Load - - -KEY_SHEAR_BP = 'Load.Shear_BP' -KEY_DISP_SHEAR_BP = 'Shear Force (kN)' -KEY_SHEAR_MAJOR = 'Load.Shear.Major' -KEY_DISP_SHEAR_MAJOR = ' - Along major axis (z-z)' -KEY_SHEAR_MINOR = 'Load.Shear.Minor' -KEY_DISP_SHEAR_MINOR = ' - Along minor axis (y-y)' - - -################################### -# Key for Storing Axial sub-key of Load -KEY_AXIAL_BP = 'Load.Axial_Compression' -KEY_DISP_AXIAL_BP = 'Axial Compression (kN)' -KEY_AXIAL_TENSION_BP = 'Load.Axial_Tension' -KEY_DISP_AXIAL_TENSION_BP = 'Axial Tension/Uplift (kN)' -KEY_DISP_DP_BOLT_HOLE_TYPE = 'Hole Type' - -# KEY_PC = 'Bolt.PC' -KEY_DISP_PC = 'Property Class' -KEY_DISP_DP_BOLT_MATERIAL_G_O = 'Material grade overwrite (MPa) Fu' -KEY_DISP_DP_BOLT_DESIGN_PARA = 'HSFG Bolt:' - - -KEY_DISP_DP_BOLT_SLIP_FACTOR = 'Slip Factor, (muf)' -KEY_DISP_DP_BOLT_SLIP_FACTOR_REPORT = 'Slip Factor, ($\mu_{f}$)' -KEY_DISP_DP_BOLT_FU = 'Bolt Ultimate Strength (N/mm2)' -KEY_DISP_DP_BOLT_FY = 'Bolt Yield Strength (N/mm2)' -KEY_DISP_GAMMA_M0 = "Governed by Yielding" -KEY_DISP_GAMMA_M1 = "Governed by Ultimate Stress" -KEY_DISP_GAMMA_MB = "Connection Bolts - Bearing Type" -KEY_DISP_GAMMA_MF = "Connection Bolts - Friction Type" -KEY_DISP_GAMMA_MW = "Connection Weld" - - -KEY_DISP_DP_WELD_TYPE = 'Weld Type' -KEY_DISP_BEAM_FLANGE_WELD_TYPE = 'Beam Flange to End Plate' -KEY_DISP_BEAM_WEB_WELD_TYPE = 'Beam Web to End Plate' -KEY_DISP_STIFFENER_WELD_TYPE = "Stiffener" -KEY_DISP_CONTINUITY_PLATE_WELD_TYPE = "Continuity Plate" -KEY_DP_WELD_TYPE_FILLET = 'Fillet Weld' -KEY_DP_WELD_TYPE_GROOVE = 'Groove Weld' -KEY_DP_WELD_TYPE_VALUES = [KEY_DP_WELD_TYPE_FILLET, KEY_DP_WELD_TYPE_GROOVE] - -KEY_DISP_DP_WELD_FAB = 'Type of Weld Fabrication' -KEY_DP_FAB_SHOP = 'Shop Weld' -KEY_DP_FAB_FIELD = 'Field weld' -KEY_DP_WELD_FAB_VALUES = [KEY_DP_FAB_SHOP, KEY_DP_FAB_FIELD] - -KEY_DISP_DP_WELD_MATERIAL_G_O = 'Material Grade Overwrite, Fu (MPa)' -KEY_DISP_DP_WELD_MATERIAL_G_O_REPORT = 'Material Grade Overwrite, $F_{u}$ (MPa)' -KEY_DP_DESIGN_BASE_PLATE = 'DesignPreferences.Design.Base_Plate' -# KEY_DISP_DP_DETAILING_EDGE_TYPE = 'Type of edge' -KEY_DISP_DP_DETAILING_EDGE_TYPE = 'Edge Preparation Method' # added by Danish Ansari - -DISP_TITLE_INTERMITTENT = 'Intermittent Connection' -DISP_TITLE_BOLTD = 'Bolt Details' -DISP_TITLE_PLATED = 'Plate Details' - -KEY_DISP_DP_DETAILING_GAP = 'Gap Between Beam and
Support (mm)' -KEY_DISP_DP_DETAILING_GAP_BEAM = 'Gap Between Beams (mm)' -KEY_DISP_DP_DETAILING_GAP_COL = 'Gap Between Columns (mm)' -KEY_DISP_DP_DETAILING_CORROSIVE_INFLUENCES = 'Are the Members Exposed to
Corrosive Influences?' -KEY_DISP_DP_DETAILING_CORROSIVE_INFLUENCES_BEAM = 'Are the Members Exposed to Corrosive Influences?' -KEY_DISP_CORR_INFLUENCES = 'Members exposed to corrosive influences?' -KEY_DISP_DP_DESIGN_METHOD = 'Design Method' - -KEY_DISP_DP_DESIGN_BASE_PLATE = 'Base Plate Analysis' -KEY_DISP_GAP = 'Gap Between Members (mm)' - - -KEY_DISP_MECH_PROP = 'Mechanical Properties' -KEY_DISP_DIMENSIONS = 'Dimensions' -KEY_DISP_DEPTH = 'Depth, D (mm)*' -KEY_DISP_WIDTH = 'Width, B (mm)*' -KEY_DISP_THICKNESS = 'Thickness, T (mm)*' -KEY_DISP_NB = 'Nominal Bore, NB (mm)*' -KEY_DISP_OD = 'Outside Diameter, OD (mm)*' -KEY_DISP_FLANGE_W = 'Flange Width, B (mm)*' -KEY_DISP_FLANGE_T = 'Flange Thickness, T (mm)*' -KEY_DISP_WEB_HEIGHT = 'Web Height, D (mm*)' -KEY_DISP_WEB_T = 'Web Thickness, t (mm)*' -KEY_DISP_FLANGE_S = 'Flange Slope, α (deg.)*' -KEY_DISP_FLANGE_S_REPORT = 'Flange Slope' -KEY_DISP_ROOT_R = 'Root Radius, R1 (mm)*' -KEY_DISP_TOE_R = 'Toe Radius, R2 (mm)*' -KEY_DISP_TYPE = 'Type' -KEY_DISP_MOD_OF_ELAST = 'Modulus of Elasticity, E (GPa)' -KEY_DISP_MOD_OF_RIGID = 'Modulus of Rigidity, G (GPa)' -KEY_DISP_SEC_PROP = 'Section Properties' -KEY_DISP_MASS = 'Mass, M (Kg/m)' -KEY_DISP_Cz = 'Cz (cm)' -KEY_DISP_Cy = 'Cy (cm)' -KEY_DISP_AREA = 'Sectional Area, a (cm2)' -KEY_DISP_MOA = '2nd Moment of Area, I (cm4/m)*' -KEY_DISP_MOA_IZ = '2nd Moment of Area, Iz (cm4)' -KEY_DISP_MOA_IY = '2nd Moment of Area, Iy (cm4)' -KEY_DISP_MOA_IU = '2nd Moment of Area, Iu (cm4)' -KEY_DISP_MOA_IV = '2nd Moment of Area, Iv (cm4)' -KEY_DISP_ROG = 'Radius of Gyration, r (cm)*' -KEY_DISP_ROG_RZ = 'Radius of Gyration, rz (cm)' -KEY_DISP_ROG_RY = 'Radius of Gyration, ry (cm)' -KEY_DISP_ROG_RU = 'Radius of Gyration, ru (cm)' -KEY_DISP_ROG_RV = 'Radius of Gyration, rv (cm)' -KEY_DISP_SM = 'Section Modulus, Z (cm3)*' -KEY_DISP_EM_ZZ = 'Elastic Modulus, Zz (cm3)' -KEY_DISP_EM_ZY = 'Elastic Modulus, Zy (ccm3)' -KEY_DISP_PM_ZPZ = 'Plastic Modulus, Zpz (cm3)' -KEY_DISP_PM_ZPY = 'Plastic Modulus, Zpy (cm3)' -KEY_DISP_It = 'Torsion Constant, It (cm4)' -KEY_DISP_Iw = 'Warping Constant, Iw (cm6)' -KEY_DISP_IV = 'Internal Volume (cm3/m)*' - -KEY_SOURCE = 'Section.Source' -KEY_DISP_SOURCE = 'Source' -KEY_DISP_POISSON_RATIO = 'Poisson\'s Ratio, v' -KEY_DISP_THERMAL_EXP = 'Thermal Expansion Coefficient,
(x10-6/ 0C)' -KEY_DISP_A= 'Long Leg, A (mm)*' -KEY_DISP_B= 'Short Leg, B (mm)*' -KEY_DISP_LEG_THK = 'Leg Thickness, t (mm)*' -KEY_DISP_BASE_PLATE_MATERIAL = 'Material' -KEY_DISP_ST_SK_MATERIAL = 'Material ' -KEY_DISP_REPORT_MATERIAL_GRADE = 'Material Grade, $F_{u}$ (MPa)' -KEY_DISP_BASE_PLATE_FU = 'Ultimate Strength, Fu (MPa)' -KEY_DSIP_BASE_PLATE_FY = 'Yield Strength , Fy (MPa)' -KEY_DISP_ST_SK_FU = 'Ultimate Strength, Fu (MPa)' -KEY_DSIP_ST_SK_FY = 'Yield Strength , Fy (MPa)' -KEY_DISP_ULTIMATE_STRENGTH_REPORT = 'Ultimate Strength, $F_u$ (MPa)' -KEY_DISP_YIELD_STRENGTH_REPORT = 'Yield Strength, $F_y$ (MPa)' - -# Common keys for design report - -# section properties (In the form of LaTeX equations) -KEY_REPORT_MASS = 'Mass, $m$ (kg/m)' -KEY_REPORT_AREA = 'Area, $A$ (cm$^2$)' -KEY_REPORT_DEPTH = '$D$ (mm)' -KEY_REPORT_WIDTH = '$B$ (mm)' -KEY_REPORT_MAX_LEG_SIZE = '$A$ (mm)' -KEY_REPORT_MIN_LEG_SIZE = '$B$ (mm)' -KEY_REPORT_FLANGE_THK = '$T$ (mm)' -KEY_REPORT_WEB_THK = '$t$ (mm)' -KEY_REPORT_ANGLE_THK = '$t$ (mm)' -KEY_REPORT_R1 = '$R_1$ (mm)' -KEY_REPORT_R2 = '$R_2$ (mm)' -KEY_REPORT_CY = '$C_y$ (mm)' -KEY_REPORT_CZ = '$C_z$ (mm)' -KEY_REPORT_IZ = '$I_z$ (cm$^4$)' -KEY_REPORT_IY = '$I_y$(cm$^4$)' -KEY_REPORT_IU = '$I_u$ (cm$^4$)' -KEY_REPORT_IV = '$I_v$(cm$^4$)' -KEY_REPORT_RZ = '$r_z$ (cm)' -KEY_REPORT_RY = '$r_y$ (cm)' -KEY_REPORT_RU = '$r_u$ (cm)' -KEY_REPORT_RV = '$r_v$ (cm)' -KEY_REPORT_ZEZ = '$Z_z$ (cm$^3$)' -KEY_REPORT_ZEY = '$Z_y$ (cm$^3$)' -KEY_REPORT_ZPZ = '$Z_{pz}$ (cm$^3$)' -KEY_REPORT_ZPY = '$Z_{py}$ (cm$^3$)' -KEY_REPORT_2ND_MOM = '2nd Moment of area, I ($cm^{4}/m$)' -KEY_REPORT_RADIUS_GYRATION = 'Radius of gyration, r ($cm$)' -KEY_REPORT_SECTION_MODULUS = 'Modulus of section, Z ($cm^{3}$)' -KEY_REPORT_NB = 'Nominal bore, NB (mm)' -KEY_REPORT_OD = 'Out diameter, OD (mm)' - -# Design cheks -KEY_REPORT_DIAMETER = 'Diameter $(mm)$' -KEY_REPORT_BOLT_NOS = 'Number of Bolts' -KEY_REPORT_PROPERTY_CLASS = 'Property Class' -KEY_REPORT_MIN_END = 'Min. End Distance $(mm)$' -KEY_REPORT_MAX_END = 'Max. End Distance $(mm)$' -KEY_REPORT_MIN_EDGE = 'Min. Edge Distance $(mm)$' -KEY_REPORT_MAX_EDGE = 'Max. Edge Distance $(mm)$' -KEY_REPORT_MIN_PITCH = 'Min. Pitch Distance $(mm)$' -KEY_REPORT_MAX_PITCH = 'Max. Pitch Distance $(mm)$' -KEY_REPORT_MIN_GAUGE = 'Min. Gauge Distance $(mm)$' -KEY_REPORT_MAX_GAUGE = 'Max. Gauge Distance $(mm)$' - -KEY_REPORT_PLATE_LENGTH = 'Length $(mm)$' -KEY_REPORT_PLATE_WIDTH = 'Width $(mm)$' -KEY_REPORT_PLATE_HEIGHT = 'Height $(mm)$' - -KEY_REPORT_SHEAR_CAPA = 'Shear Capacity $(kN)$' -KEY_REPORT_BEARING_CAPA = 'Bearing Capacity $(kN)$' -KEY_REPORT_BOLT_CAPA = 'Bolt Capacity $(kN)$' -KEY_REPORT_TENSION_CAPA = 'Tension Capacity $(kN)$' -KEY_REPORT_TENSION_DEMAND = 'Tension Demand $(kN)$' - -######################## -# Output Keys -######################## -KEY_OUT_ANCHOR_BOLT_SHEAR = 'Anchor Bolt.Shear' -KEY_OUT_ANCHOR_BOLT_BEARING = 'Anchor Bolt.Bearing' -KEY_OUT_ANCHOR_BOLT_CAPACITY = 'Anchor Bolt.Capacity' -KEY_OUT_ANCHOR_BOLT_COMBINED = 'Anchor Bolt.Combined' -KEY_OUT_ANCHOR_BOLT_TENSION_DEMAND = 'Anchor Bolt.Tension_Demand' -KEY_OUT_ANCHOR_BOLT_TENSION = 'Anchor Bolt.Tension' -KEY_MEMBER_CAPACITY = "section.memcapacity" -KEY_MEMBER_AXIALCAPACITY='Section.AxialCapacity' -KEY_MEMBER_SHEAR_CAPACITY='Section.ShearCapacity' -KEY_MEMBER_MOM_CAPACITY='Section.MomCapacity' -KEY_OUT_BASEPLATE_THICKNNESS = 'Baseplate.Thickness' -KEY_OUT_BASEPLATE_LENGTH = 'Baseplate.Length' -KEY_OUT_BASEPLATE_WIDTH = 'Baseplate.Width' -KEY_OUT_BASEPLATE_BEARING_STRESS = 'Baseplate.BearingStress' -KEY_OUT_BASEPLATE_MOMENT_DEMAND = 'Baseplate.MomentDemand' -KEY_OUT_DISP_BASEPLATE_MOMENT_DEMAND = 'Moment Demand (kNm)' -KEY_OUT_BASEPLATE_MOMENT_CAPACITY = 'Baseplate.MomentCapacity' -KEY_OUT_DISP_BASEPLATE_MOMENT_CAPACITY = 'Moment Capacity (kNm)' -# KEY_OUT_DISP_BASEPLATE_BEARING_STRESS = 'Bearing Stress (N/mm2)' -KEY_OUT_DISP_BASEPLATE_BEARING_STRESS = 'Bearing Stress (MPa)' -KEY_OUT_DETAILING_PROJECTION = 'Detailing.Projection' -KEY_OUT_DETAILING_NO_OF_ANCHOR_BOLT = 'Detailing.No of Anchor bolts' -KEY_OUT_DETAILING_EDGE_DISTANCE = 'Detailing.EdgeDistanceOut' -KEY_IN_DETAILING_EDGE_DISTANCE = 'Detailing.EdgeDistanceIn' -KEY_OUT_DETAILING_GAUGE_DISTANCE = 'Detailing.GaugeDistanceOut' -KEY_IN_DETAILING_GAUGE_DISTANCE = 'Detailing.GaugeDistanceIn' -KEY_OUT_DETAILING_CS_GAUGE_DISTANCE = 'Detailing.Cross-centre Gauge Distance' -KEY_OUT_DETAILING_PITCH_DISTANCE = 'Detailing.PitchDistanceOut' -KEY_IN_DETAILING_PITCH_DISTANCE = 'Detailing.PitchDistanceIn' -KEY_BOLT_FU = 'Bolt.fu' -KEY_BOLT_FY = 'Bolt.fy' - -KEY_OUT_DISP_DETAILING_BOLT_COLUMNS = 'Detailing.No. of Columns' -KEY_OUT_DISP_DETAILING_BOLT_COLUMNS_EP = 'No. of Columns' -KEY_OUT_DISP_DETAILING_BOLT_ROWS = 'Detailing.No. of Rows' -KEY_OUT_DISP_DETAILING_BOLT_ROWS_EP = 'No. of Rows' -KEY_OUT_DISP_DETAILING_BOLT_NUMBERS = 'Detailing.No. of Bolts' -KEY_OUT_DISP_DETAILING_BOLT_NUMBERS_EP = 'No. of Bolts' - - -KEY_OUT_GUSSET_PLATE_THICKNNESS = 'GussetPlate.Thickness' -KEY_OUT_GUSSET_PLATE_SHEAR_DEMAND = 'GussetPlate.Shear_Demand' -KEY_OUT_GUSSET_PLATE_SHEAR = 'GussetPlate.Shear' -KEY_OUT_GUSSET_PLATE_MOMENT_DEMAND = 'GussetPlate.Moment_Demand' -KEY_OUT_GUSSET_PLATE_MOMENT = 'GussetPlate.Moment' -KEY_OUT_STIFFENER_PLATE_THICKNNESS = 'StiffenerPlate.Thickness' - -KEY_OUT_STIFFENER_PLATE_SHEAR_DEMAND = 'StiffenerPlate.Shear_Demand' -KEY_OUT_STIFFENER_PLATE_SHEAR_DEMAND_CHS = 'StiffenerPlate.Shear_Demand' -KEY_OUT_STIFFENER_PLATE_SHEAR_CAPACITY = 'StiffenerPlate.Shear_Capacity' -KEY_OUT_STIFFENER_PLATE_SHEAR_CAPACITY_CHS = 'StiffenerPlate.Shear_Capacity' -KEY_OUT_STIFFENER_PLATE_SHEAR = 'StiffenerPlate.Shear' -KEY_OUT_STIFFENER_PLATE_MOMENT_DEMAND = 'StiffenerPlate.Moment_Demand' -KEY_OUT_STIFFENER_PLATE_MOMENT_DEMAND_CHS = 'StiffenerPlate.Moment_Demand' -KEY_OUT_STIFFENER_PLATE_MOMENT_CAPACITY = 'StiffenerPlate.Moment_Capacity' -KEY_OUT_STIFFENER_PLATE_MOMENT_CAPACITY_CHS = 'StiffenerPlate.Moment_Capacity' -KEY_OUT_STIFFENER_PLATE_MOMENT = 'StiffenerPlate.Moment' - -KEY_PLATE_MIN_HEIGHT = 'Plate.MinHeight' -KEY_PLATE_MAX_HEIGHT = 'Plate.MaxHeight' -KEY_SLENDER = "Member.Slenderness" - -KEY_INNERFLANGEPLATE_THICKNESS = 'flange_plate.innerthickness_provided' -KEY_FLANGE_PLATE_HEIGHT = 'Flange_Plate.Width (mm)' -KEY_OUT_FLANGESPLATE_THICKNESS = 'flange_plate.Thickness' -KEY_DISP_FLANGESPLATE_THICKNESS = 'Thickness (mm)' -KEY_FLANGE_PLATE_LENGTH ='flange_plate.Length' -KEY_OUT_FLANGE_BOLT_SHEAR ="flange_bolt.shear capacity" - -KEY_INNERPLATE= "flange_plate.Inner_plate_details" - -KEY_INNERFLANGE_PLATE_HEIGHT = 'Flange_Plate.InnerWidth' -KEY_INNERFLANGE_PLATE_LENGTH ='flange_plate.InnerLength' - -KEY_DISP_AREA_CHECK ="Plate Area Check (mm2)" - - -KEY_FLANGE_SPACING ="Flange_plate.spacing" - -KEY_FLANGE_PITCH = 'Flange_plate.pitch_provided' -KEY_FLANGE_PLATE_GAUGE = "Flange_plate.gauge_provided " -KEY_ENDDIST_FLANGE= 'Flange_plate.end_dist_provided ' -KEY_EDGEDIST_FLANGE= 'Flange_plate.edge_dist_provided' - -KEY_FLANGE_CAPACITY ='section.flange_capacity' - -# flange -KEY_FLANGE_TEN_CAPACITY ="Section.flange_capacity" -KEY_DISP_FLANGE_TEN_CAPACITY ="Flange Tension Capacity (kN)" -KEY_TENSIONYIELDINGCAP_FLANGE = 'section.tension_yielding_capacity' -KEY_DISP_TENSIONYIELDINGCAP_FLANGE = 'Flange Tension Yielding Capacity (kN)' -KEY_TENSIONRUPTURECAP_FLANGE='section.tension_rupture_capacity ' -KEY_DISP_TENSIONRUPTURECAP_FLANGE= 'Flange Tension Rupture Capacity (kN)' -KEY_BLOCKSHEARCAP_FLANGE='section.block_shear_capacity' -KEY_DISP_BLOCKSHEARCAP_FLANGE='Flange Block Shear Capacity (kN)' -# flange plate -KEY_TENSIONYIELDINGCAP_FLANGE_PLATE = 'Flange_plate.tension_yielding_capacity (kN)' -KEY_DISP_TENSIONYIELDINGCAP_FLANGE_PLATE ='Tension Yielding Capacity (kN)' -KEY_TENSIONRUPTURECAP_FLANGE_PLATE= 'Flange_plate.tension_rupture_capacity (kN)' -KEY_DISP_TENSIONRUPTURECAP_FLANGE_PLATE ='Tension Rupture Capacity (kN)' -KEY_BLOCKSHEARCAP_FLANGE_PLATE = 'flange_plate.block_shear_capacity ' -KEY_DISP_BLOCKSHEARCAP_FLANGE_PLATE ='Block Shear Capacity (kN)' -KEY_FLANGE_PLATE_TEN_CAP ="flange_plate.tension_capacity_flange_plate" - - - -# KEY_TENSIONRUPTURECAP_FLANGE= 'Flange_plate.tension_rupture_capacity' -# KEY_DISP_TENSIONRUPTURECAP_FLANGE= 'Flange Tension Rupture Capacity (kN)' -# KEY_SHEARYIELDINGCAP_FLANGE= 'Flange_plate.shear_yielding_capacity' -# KEY_DISP_SHEARYIELDINGCAP_FLANGE= 'Shear Yielding Capacity (kN)' -# KEY_SHEARRUPTURECAP_FLANGE= 'Flange_plate.shear_rupture_capacity' -# KEY_DISP_SHEARRUPTURECAP_FLANGE= 'Shear Rupture Capacity (kN)' -KEY_FLANGE_PLATE_MOM_DEMAND = 'Flange_Plate.MomDemand' -KEY_FLANGE_DISP_PLATE_MOM_DEMAND = 'Flange Moment Demand (kNm)' -KEY_FLANGE_PLATE_MOM_CAPACITY='Flange_plate.MomCapacity' -KEY_FLANGE_DISP_PLATE_MOM_CAPACITY = 'Flange Moment Capacity (kNm)' -KEY_DESIGNATION = "section_size.designation" -KEY_DISP_DESIGNATION = "Designation" - - -KEY_TENSION_YIELDCAPACITY = "Member.tension_yielding" -KEY_DISP_TENSION_YIELDCAPACITY = 'Tension Yielding Capacity (kN)' -KEY_TENSION_RUPTURECAPACITY = "Member.tension_rupture" -KEY_DISP_TENSION_RUPTURECAPACITY = 'Tension Rupture Capacity (kN)' -KEY_TENSION_BLOCKSHEARCAPACITY = "Member.tension_blockshear" -KEY_DISP_TENSION_BLOCKSHEARCAPACITY = 'Block Shear Capacity (kN)' - -KEY_SHEAR_YIELDCAPACITY = "Member.shear_yielding" -KEY_SHEAR_RUPTURECAPACITY = "Member.shear_rupture" -KEY_SHEAR_BLOCKSHEARCAPACITY = "Member.shear_blockshear" - - - -KEY_TENSION_CAPACITY = "Member.tension_capacity" -KEY_DISP_TENSION_CAPACITY = "Tension Capacity (kN)" - -KEY_EFFICIENCY = "Member.efficiency" -KEY_DISP_EFFICIENCY = "Utilization Ratio" - -DISP_TITLE_BOLTDETAILS ='Bolt Details' -KEY_BOLT_DETAILS ="Bolt.Details" - -DISP_TITLE_BOLT_CAPACITIES = 'Bolt Capacities' -KEY_BOLT_CAPACITIES = 'Bolt.Capacities' -DISP_THROAT_THICKNESS = "Throat Thickness" -DISP_TITLE_BOLT_CAPACITY_FLANGE= 'Flange Bolt Capacity' -KEY_DISP_BOLT_DETAILS = "Bolt Details" -KEY_FLANGE_BOLT_LINE = 'Flange_plate.Bolt_Line' -KEY_FLANGE_DISP_BOLT_LINE = 'Bolt Lines ' -KEY_FLANGE_BOLTS_ONE_LINE = 'Flange_plate.Bolt_OneLine' -KEY_FLANGE_DISP_BOLTS_ONE_LINE = 'Bolts in One Line ' -KEY_FLANGE_BOLTS_REQ = "Flange_plate.Bolt_required" -KEY_FLANGE_DISP_BOLTS_REQ = "Bolts Required" -KEY_FLANGE_NUM_BOLTS_REQ = "Flange_plate.Bolt_required" - - -KEY_FLANGE_WELD_DETAILS = "Flange detail" -KEY_DISP_FLANGE_WELD_DETAILS = "Weld Details" - -KEY_INNERFLANGE_WELD_DETAILS = "Inner Flange detail" -KEY_DISP_INNERFLANGE_WELD_DETAILS = "Weld Details" - -KEY_WELD_TYPE = 'Weld.Type' -KEY_DISP_WELD_TYPE = 'Type' -VALUES_WELD_TYPE = ["Fillet Weld", "Groove Weld"] -VALUES_WELD_TYPE_EP = ["Groove Weld", "Fillet Weld"] -VALUES_WELD_TYPE_BB_FLUSH = ["Groove Weld"] -DISP_FLANGE_TITLE_WELD = 'Flange Weld' -KEY_FLANGE_WELD_SIZE = 'Flange_Weld.Size' -KEY_FLANGE_DISP_WELD_SIZE = 'Flange Weld Size (mm)' -KEY_FLANGE_WELD_STRENGTH = 'Flange_Weld.Strength' -KEY_FLANGE_DISP_WELD_STRENGTH = 'Flange Weld Strength (N/mm)' -KEY_FLANGE_WELD_STRESS = 'Flange_Weld.Stress' -KEY_FLANGE_DISP_WELD_STRESS = 'Flange Weld Stress (N/mm)' -KEY_FLANGE_WELD_LENGTH = 'Flange_Weld.Length' -KEY_DISP_FLANGE_WELD_LENGTH ='Flange Weld Length' -KEY_FLANGE_WELD_LENGTH_EFF = 'Flange_Weld.EffLength' - -KEY_DISP_WELD_LEN_EFF_OUTSIDE = 'EffLength. Outer+Inner flange' -KEY_DISP_CLEARANCE = "Clearance (mm)" -KEY_FLANGE_WELD_HEIGHT ='flange_Weld.height' -KEY_DISP_FLANGE_WELD_HEIGHT = 'Flange Weld Height' -DISP_EFF = "Effective Length (mm)" -KEY_INNERFLANGE_WELD_LENGTH = 'Flange_Weld.InnerLength' -KEY_DISP_INNERFLANGE_WELD_LENGTH ='Length (mm)' -KEY_INNERFLANGE_WELD_LENGTH_EFF = 'Flange_Weld.InnerEffLength' -KEY_INNERFLANGE_WELD_HEIGHT ='flange_Weld.Innerheight' -KEY_DISP_INNERFLANGE_WELD_HEIGHT = 'Height (mm)' -KEY_INNERFLANGE_WELD_STRESS = 'Inner_Flange_Weld.Stress' -KEY_INNERFLANGE_DISP_WELD_STRESS = 'Flange Weld Stress (N/mm)' -KEY_INNERFLANGE_WELD_STRENGTH = 'Inner_Flange_Weld.Strength' -KEY_INNERFLANGE_DISP_WELD_STRENGTH = 'Flange Weld Strength (N/mm)' - -# FLANGE AND WEB -REDUCTION FACTOR -KEY_REDUCTION_FACTOR_LONG_FLANGE ='flange_plate.red,factor' -KEY_DISP_REDUCTION_FACTOR_FLANGE ="Long Joint Red.Factor" - -KEY_REDUCTION_FACTOR_LONG_WEB ='web_plate.red,factor' -KEY_DISP_REDUCTION_FACTOR_LONG_WEB ="Long Joint Red.Factor" - -KEY_REDUCTION_LARGE_GRIP_WEB = 'web_bolt.large_grip' -KEY_DISP_REDUCTION_LARGE_GRIP_WEB = "Large Grip Red.Factor" - -KEY_REDUCTION_LARGE_GRIP_FLANGE = 'flange_bolt.large_grip' -KEY_DISP_REDUCTION_LARGE_GRIP_FLANGE = "Large Grip Red.Factor" - -# COMMON -REDUCTION FACTOR -KEY_REDUCTION_LONG_JOINT ="bolt.long_joint" -KEY_DISP_REDUCTION_LONG_JOINT ="Long Joint Red.Factor" - -KEY_REDUCTION_LARGE_GRIP ="bolt.large_grip" -KEY_DISP_REDUCTION_LARGE_GRIP ="Large Grip Red.Factor" - - - -KEY_DISP_REDUCTION ="Strength Red.Factor" -KEY_OUT_FLANGE_BOLT_SHEAR ='flange_bolt.bolt_shear_capacity' -KEY_OUT_DISP_FLANGE_BOLT_SHEAR = "Shear Capacity (kN)" -KEY_OUT_FLANGE_BOLT_BEARING = 'flange_bolt.bolt_bearing_capacity' -KEY_OUT_DISP_FLANGE_BOLT_BEARING = "Bearing Capacity (kN)" -KEY_OUT_FLANGE_BOLT_CAPACITY = 'flange_bolt.bolt_capacity' -KEY_OUT_DISP_FLANGE_BOLT_CAPACITY ="Bolt Capacity (kN)" -KEY_OUT_DISP_FLANGE_BOLT_SLIP= 'Slip Resistance (kN)' -KEY_FLANGE_BOLT_GRP_CAPACITY = 'flange_bolt.grp_bolt_capacity' -KEY_OUT_FLANGE_BOLT_GRP_CAPACITY = 'flange bolt grp bolt capacity (kN)' -KEY_OUT_MIN_PITCH= 'Min_pitch' - -KEY_OUT_FLANGE_MIN_PITCH= 'flange_bolt.min_pitch_round' -KEY_OUT_FLANGE_MIN_EDGE_DIST= 'flange_bolt.min_edge_dist_round' -KEY_OUT_FLANGE_MAX_EDGE_DIST='flange_bolt.max_edge_dist_round' - -KEY_OUT_DISP_FORCES_FLANGE = 'Force Carried by Flange' -KEY_OUT_DISP_FORCES_WEB= 'Force Carried by Web' -KEY_OUT_WEB_BOLT_SHEAR ='web_bolt.bolt_shear_capacity' -KEY_OUT_DISP_WEB_BOLT_SHEAR = "Shear Capacity (kN)" -KEY_OUT_WEB_BOLT_BEARING = 'web_bolt.bolt_bearing_capacity' -KEY_OUT_DISP_WEB_BOLT_BEARING = "Bearing Capacity (kN)" -KEY_OUT_WEB_BOLT_CAPACITY = 'web_bolt.bolt_capacity' -KEY_OUT_DISP_WEB_BOLT_CAPACITY ="Bolt Capacity (kN)" -KEY_OUT_DISP_WEB_BOLT_SLIP= 'Slip Resistance (kN)' -KEY_WEB_BOLT_GRP_CAPACITY = 'web_bolt.grp_bolt_capacity' -KEY_OUT_WEB_BOLT_GRP_CAPACITY = 'Web bolt grp bolt capacity (kN)' -KEY_OUT_REQ_MOMENT_DEMAND_BOLT = "Moment Demand (kNm)" -KEY_OUT_REQ_PARA_BOLT = "Bolt Force Parameter(s) (mm)" -DISP_TITLE_WEBSPLICEPLATE = 'Web Splice Plate' -KEY_DISP_WEBPLATE_THICKNESS = 'Thickness (mm)*' - - - - -KEY_WEB_PLATE_HEIGHT = 'Web_Plate.Height (mm)' -KEY_DISP_WEB_PLATE_HEIGHT = 'Height (mm)' -KEY_WEB_PLATE_LENGTH ='Web_Plate.Width' -KEY_OUT_WEBPLATE_THICKNESS = 'Web_Plate.Thickness' -KEY_DISP_WEBPLATE_THICKNESS = 'Thickness (mm)' -KEY_DISP_WEB_PLATE_LENGTH ='Width (mm)' -DISP_TITLE_BOLT_CAPACITY_WEB = 'Web Bolt Capacity' -KEY_BOLT_CAPACITIES_WEB = 'Web Bolt.Capacities' - -KEY_WEB_SPACING ="Web_plate.spacing" -KEY_DISP_WEB_SPACING = 'Spacing (mm)' -KEY_WEB_PITCH = "Web_plate.pitch_provided" -KEY_DISP_WEB_PLATE_PITCH ="Pitch Distance (mm)" -KEY_WEB_GAUGE = "Web_plate.gauge_provided " -KEY_DISP_WEB_PLATE_GAUGE ="Gauge Distance (mm)" -KEY_ENDDIST_W= 'Web_plate.end_dist_provided ' -KEY_DISP_END_DIST_W = 'End Distance (mm)' -KEY_EDGEDIST_W = 'Web_plate.edge_dist_provided' -KEY_DISP_EDGEDIST_W = 'Edge Distance (mm)' - -KEY_WEB_CAPACITY ='section.web_capacities' -KEY_DISP_WEB_CAPACITY ='Capacity' - -# Web plate -KEY_REDUCTION_FACTOR_WEB ='web_plate.red,factor' -KEY_DISP_REDUCTION_FACTOR_WEB ="Red. Factor" -KEY_WEB_PLATE_CAPACITY ="Web_plate.capacity" -KEY_DISP_WEB_PLATE_CAPACITY= 'Web Plate Tension Capacity (kN)' -KEY_TEN_YIELDCAPACITY_WEB_PLATE = "Web_plate.tension_yielding" -KEY_DISP_TENSION_YIELDCAPACITY_WEB_PLATE = 'Tension Yielding Capacity (kN)' -KEY_TENSION_RUPTURECAPACITY_WEB_PLATE = "Web_plate.tension_rupture" -KEY_DISP_TENSION_RUPTURECAPACITY_WEB_PLATE= 'Tension Rupture Capacity (kN)' -KEY_TENSION_BLOCKSHEARCAPACITY_WEB_PLATE = "Web_plate.tension_blockshear" -KEY_DISP_TENSION_BLOCKSHEARCAPACITY_WEB_PLATE = 'Block Shear Capacity (kN)' -# Web -KEY_TENSIONYIELDINGCAP_WEB = "section.tension_yielding_capacity_web" -KEY_DISP_TENSIONYIELDINGCAP_WEB ='Web Tension Yielding Capacity (kN)' -KEY_TENSIONRUPTURECAP_WEB ='section.tension_rupture_capacity_web' -KEY_DISP_TENSIONRUPTURECAP_WEB ='Web Tension Rupture Capacity (kN)' -KEY_TENSIONBLOCK_WEB ='section.block_shear_capacity_web' -KEY_DISP_BLOCKSHEARCAP_WEB ='Web Block Shear Capacity (kN)' -KEY_WEB_TEN_CAPACITY ="section.Tension_capacity_web" -KEY_DISP_WEB_TEN_CAPACITY ="Web Tension Capacity (kN)" -# web in shear -KEY_SHEARYIELDINGCAP_WEB_PLATE= 'web_plate.shear_yielding_capacity' -KEY_DISP_SHEARYIELDINGCAP_WEB_PLATE= 'Shear Yielding Capacity (kN)' -KEY_BLOCKSHEARCAP_WEB_PLATE='web_plate.block_shear_capacity' -KEY_DISP_BLOCKSHEARCAP_WEB_PLATE='Block Shear Capacity (kN)' -KEY_SHEARRUPTURECAP_WEB_PLATE= 'web_plate.shear_rupture_capacity' -KEY_DISP_SHEARRUPTURECAP_WEB_PLATE= 'Shear Rupture Capacity (kN)' -KEY_WEBPLATE_SHEAR_CAPACITY_PLATE ="web_plate.shear_capacity_web_plate" -KEY_DISP_WEBPLATE_SHEAR_CAPACITY_PLATE ="Web Plate Shear Capacity (kN)" -KEY_WEB_PLATE_MOM_DEMAND = 'Web_Plate.MomDemand' -KEY_WEB_DISP_PLATE_MOM_DEMAND = 'Web Moment Demand (kNm)' -KEY_WEB_PLATE_MOM_CAPACITY='Web_plate.MomCapacity' -KEY_WEB_DISP_PLATE_MOM_CAPACITY = 'Moment Capacity (kNm)' -KEY_WEB_BOLT_LINE = 'Web_plate.Bolt_Line' -KEY_WEB_DISP_BOLT_LINE = 'Bolt Lines' -KEY_WEB_BOLTS_REQ = "Web_plate.Bolt_required" -KEY_WEB_DISP_BOLTS_REQ = "Bolt Required" -KEY_WEB_BOLTS_ONE_LINE = 'Web_plate.Bolt_OneLine' -KEY_WEB_DISP_BOLTS_ONE_LINE = 'Bolts in One Line' - -KEY_WEB_WELD_DETAILS = "Web detail" -KEY_DISP_WEB_WELD_DETAILS = "Weld Details" -DISP_WEB_TITLE_WELD = 'Web Weld' -KEY_WEB_WELD_SIZE = 'Web_Weld.Size' -KEY_WEB_DISP_WELD_SIZE = 'Web Weld Size (mm)' -KEY_WEB_WELD_STRENGTH = 'Web_Weld.Strength' -KEY_WEB_DISP_WELD_STRENGTH = 'Web Weld Strength (N/mm)' -KEY_WEB_WELD_STRESS = 'Web_Weld.Stress' -KEY_WEB_DISP_WELD_STRESS = 'Web Weld Stress (N/mm)' -KEY_WEB_WELD_LENGTH = 'Web_Weld.Length' -KEY_DISP_WEB_WELD_LENGTH = 'Web Weld Length' -KEY_WEB_WELD_LENGTH_EFF = 'Web_Weld.EffLength' -KEY_WEB_WELD_HEIGHT ='Web_Weld.height' -KEY_DISP_WEB_WELD_HEIGHT = 'Web Weld Height' -KEY_OUT_LONG_JOINT_WELD = 'Weld Strength (post long joint) (N/mm)' -KEY_OUT_DISP_RED_WELD_STRENGTH = 'Weld Strength (N/mm)' - - -DISP_TITLE_ENDPLATE = 'End Plate' - -KEY_ENDPLATE_THICKNESS = 'Plate.end_plate.Thickness' -KEY_DISP_ENDPLATE_THICKNESS = 'Thickness (mm)' - -KEY_BASE_PLATE_MATERIAL = 'Base_Plate.Material' -KEY_ST_KEY_MATERIAL = 'Stiffener_Key.Material' -KEY_BASE_PLATE_FU = 'Base_Plate.Fu' -KEY_BASE_PLATE_FY = 'Base_Plate.Fy' -KEY_ST_KEY_FU = 'Stiffener_Key.Fu' -KEY_ST_KEY_FY = 'Stiffener_Key.Fy' - -KEY_DISP_LEVER_ARM = "Lever Arm (mm)" -KEY_DISP_REQ_PARA= "Parameters" -KEY_BOLT_STATUS = 'Bolt.DesignStatus' -KEY_OUT_D_PROVIDED = 'Bolt.Diameter' -KEY_OUT_DISP_D_PROVIDED = 'Diameter (mm)' -KEY_OUT_DISP_D_MIN= 'Min. Diameter (mm)' -KEY_OUT_INTER_D_PROVIDED = 'Bolt.InterDiameter' -KEY_OUT_DISP_INTER_D_PROVIDED = 'Diameter (mm)' - - - - -KEY_OUT_GRD_PROVIDED = 'Bolt.Grade_Provided' -KEY_OUT_DISP_GRD_PROVIDED = 'Property Class' -KEY_OUT_INTER_GRD_PROVIDED = 'Bolt.InterGrade' -KEY_OUT_DISP_INTER_GRD_PROVIDED = 'Grade' - - - - -KEY_OUT_DISP_PC_PROVIDED = 'Property Class' -KEY_OUT_ROW_PROVIDED = 'Bolt.Rows' -KEY_OUT_DISP_ROW_PROVIDED = 'Rows of Bolts' -KEY_OUT_COL_PROVIDED = 'Bolt.Cols' -KEY_OUT_DISP_COL_PROVIDED = 'Columns of Bolts' -KEY_OUT_TOT_NO_BOLTS = 'Bolt.number' -KEY_OUT_DISP_TOT_NO_BOLTS = 'Number of Bolts' -KEY_OUT_KB = 'Bolt.Kb' -KEY_OUT_BOLT_HOLE = 'Bolt.Hole' -KEY_DISP_BOLT_HOLE = 'Hole Diameter (mm)' -KEY_DISP_MIN_BOLT = 'Minimum Bolts (nos)' - -KEY_DISP_BOLT_AREA = 'Nominal Stress Area (mm2)' -KEY_DISP_KB = 'Kb' - -KEY_OUT_BOLT_IR_DETAILS = 'Bolt.IRDetails' -KEY_OUT_BOLT_IR_DETAILS_SPTD = 'Bolt.IRDetails_sptd' -KEY_OUT_BOLT_IR_DETAILS_SPTING = 'Bolt.IRDetails_spting' -KEY_OUT_DISP_BOLT_IR_DETAILS = 'Capacity Details' -KEY_OUT_BOLT_SHEAR = 'Bolt.Shear' -KEY_OUT_DISP_BOLT_SHEAR = 'Shear Capacity (kN)' -KEY_OUT_BOLT_BEARING = 'Bolt.Bearing' -KEY_OUT_DISP_BOLT_BEARING = 'Bearing Capacity (kN)' -KEY_OUT_BETA_LJ = 'Bolt.Betalj' -KEY_OUT_DISP_BETA_LJ = 'βlj' -KEY_OUT_BETA_LG = 'Bolt.Betalg' -KEY_OUT_DISP_BETA_LG = 'βlg' -KEY_OUT_BETA_PK = 'Bolt.Betapk' -KEY_OUT_DISP_BETA_PK = 'βpk' -KEY_OUT_DISP_BOLT_SLIP= 'Slip Resistance' -KEY_OUT_DISP_BOLT_SLIP_DR = 'Slip Resistance (kN)' -KEY_OUT_BOLT_CAPACITY = 'Bolt.Capacity' -KEY_OUT_BOLT_CAPACITY_SPTD = 'Bolt.Capacity_sptd' -KEY_OUT_BOLT_CAPACITY_SPTING = 'Bolt.Capacity_spting' -KEY_OUT_DISP_BOLT_CAPACITY = 'Capacity (kN)' -KEY_OUT_DISP_BOLT_VALUE = 'Bolt Value (kN)' -KEY_OUT_BOLT_FORCE = 'Bolt.Force (kN)' -KEY_OUT_DISP_BOLT_FORCE = 'Bolt Force (kN)' -KEY_OUT_DISP_BOLT_SHEAR_FORCE = 'Bolt Shear Force (kN)' -KEY_OUT_BOLT_TENSION_FORCE = 'Bolt.TensionForce' -KEY_OUT_DISP_BOLT_TENSION_FORCE = 'Bolt Tension Force (kN)' -KEY_OUT_DISP_CRITICAL_BOLT_TENSION = 'Tension Due to Moment (kN)' -KEY_OUT_DISP_BOLT_TENSION_AXIAL = 'Tension due to Moment and Axial Force (kN)' -KEY_OUT_BOLT_PRYING_FORCE = 'Bolt.PryingForce' -KEY_OUT_DISP_BOLT_PRYING_FORCE = 'Bolt Prying Force (kN)' -KEY_OUT_DISP_BOLT_PRYING_FORCE_EP = 'Prying Force (kN)' -KEY_OUT_BOLT_TENSION_TOTAL = 'Bolt.TensionTotal' -KEY_OUT_DISP_BOLT_TENSION_TOTAL = 'Total Bolt Tension (kN)' -KEY_OUT_DISP_BOLT_TENSION_DEMAND = 'Tension Demand (kN)' -KEY_OUT_DISP_BOLT_SHEAR_DEMAND = 'Shear Demand (kN)' -KEY_OUT_BOLT_TENSION_CAPACITY = 'Bolt.Tension' -KEY_OUT_BOLT_TENSION_CAPACITY1 = 'Bolt Tension Capacity (kN)' -KEY_OUT_DISP_BOLT_TENSION_CAPACITY = 'Bolt Tension Capacity (kN)' -KEY_OUT_CRITICAL_BOLT_TENSION_CAPACITY = 'Tension Capacity (kN)' -KEY_OUT_BOLTS_REQUIRED = 'Bolt.Required' -KEY_OUT_LONG_JOINT = 'Long Joint Reduction Factor' -KEY_OUT_LARGE_GRIP = 'Large Grip Length Reduction Factor' -KEY_OUT_PACKING_PLATE = 'Packing Plate Reduction Factor' -KEY_OUT_BOLT_CAPACITY_REDUCED = 'Bolt Capacity (post reduction factor) (kN)' -KEY_OUT_BOLT_GRP_CAPACITY = 'Bolt.GroupCapacity' -KEY_OUT_BOLT_LINE = 'Bolt.Line' -KEY_OUT_DISP_BOLT_LINE = 'Bolt Columns (nos)' -KEY_OUT_INTER_BOLT_LINE = 'Bolt.InterLine' -KEY_OUT_DISP_INTER_BOLT_LINE = 'Columns (nos)' -KEY_OUT_BOLT_IR = 'Bolt.IR' -KEY_OUT_DISP_BOLT_IR = 'Interaction Ratio' -KEY_OUT_DISP_BOLT_COMBINED_CAPACITY = 'Combined Capacity, I.R' - - -KEY_OUT_BOLTS_ONE_LINE = 'Bolt.OneLine' -KEY_OUT_DISP_BOLTS_ONE_LINE = 'Bolt Rows (nos)' -KEY_OUT_BOLTS_ONE_LINE_S = 'Bolt.OneLineT' -KEY_OUT_DISP_BOLTS_ONE_LINE_S = 'Rows per Angle(nos)' - -KEY_OUT_INTER_BOLTS_ONE_LINE = 'Bolt.InterOneLine' -KEY_OUT_DISP_INTER_BOLTS_ONE_LINE = 'Rows (nos)' - - -KEY_OUT_SPACING = 'spacing' -KEY_OUT_DISP_SPACING = 'Spacing' -KEY_OUT_DISP_PATTERN = 'Pattern' -KEY_OUT_PITCH = 'Bolt.Pitch' -KEY_OUT_DISP_PITCH = 'Pitch Distance (mm)' -KEY_OUT_PATTERN_1 = 'pattern1' -KEY_OUT_PATTERN_2 = 'pattern2' - -KEY_OUT_Lw = 'Weld.Lw' -KEY_OUT_DISP_Lw = 'Lw (mm)' -KEY_OUT_Hw = 'Weld.Hw' -KEY_OUT_DISP_Hw = 'Hw (mm)' - - -KEY_OUT_END_DIST = 'Bolt.EndDist' -KEY_OUT_DISP_END_DIST = 'End Distance (mm)' -KEY_OUT_GAUGE = 'Bolt.Gauge' -KEY_OUT_DISP_GAUGE = 'Gauge Distance (mm)' -KEY_OUT_GAUGE1 = 'Bolt.Gauge1' -KEY_OUT_DISP_GAUGE1 = 'Gauge Distance 1 (mm)' -KEY_OUT_GAUGE2 = 'Bolt.Gauge2' -KEY_OUT_DISP_GAUGE2 = 'Gauge Distance 2 (mm)' -KEY_OUT_GAUGE_CENTRAL = 'Bolt.GaugeCentral' -KEY_OUT_DISP_GAUGE_CENTRAL = 'Central Gauge (mm)' - -KEY_OUT_MIN_GAUGE = 'Bolt.MinGauge' -KEY_OUT_MAX_SPACING = 'Bolt.MaxGauge' - -KEY_OUT_EDGE_DIST = 'Bolt.EdgeDist' -KEY_OUT_MIN_EDGE_DIST = 'Bolt.MinEdgeDist' -KEY_OUT_MAX_EDGE_DIST = 'Bolt.MaxEdgeDist' - - -KEY_OUT_DISP_EDGE_DIST = 'Edge Distance (mm)' - - -KEY_OUT_SPTING_BOLT_SHEAR = 'Cleat.Spting_leg.Shear' -KEY_OUT_SPTING_BOLT_BEARING = 'Cleat.Spting_leg.Bearing' -KEY_OUT_SPTING_BOLT_CAPACITY = 'Cleat.Spting_leg.Capacity' -KEY_OUT_SPTING_BOLT_FORCE = 'Cleat.Spting_leg.Force' -KEY_OUT_SPTING_BOLT_LINE = 'Cleat.Spting_leg.Line' -KEY_OUT_SPTING_BOLTS_REQUIRED = 'Cleat.Spting_leg.Required' - -KEY_OUT_SPTING_BOLT_GRP_CAPACITY = 'Cleat.Spting_leg.GroupCapacity' - -KEY_OUT_SPTING_BOLTS_ONE_LINE = 'Cleat.Spting_leg.OneLine' - -KEY_OUT_SPTING_SPACING = 'Cleat.Spting_leg.spacing' - -KEY_OUT_SPTING_PITCH = 'Cleat.Spting_leg.Pitch' - -KEY_OUT_SPTING_MIN_PITCH = 'Cleat.Spting_leg.MinPitch' -KEY_OUT_SPTING_END_DIST = 'Cleat.Spting_leg.EndDist' -KEY_OUT_SPTING_GAUGE = 'Cleat.Spting_leg.Gauge' -KEY_OUT_SPTING_MIN_GAUGE = 'Cleat.Spting_leg.MinGauge' -KEY_OUT_SPTING_MAX_SPACING = 'Cleat.Spting_leg.MaxGauge' -KEY_OUT_SPTING_EDGE_DIST = 'Cleat.Spting_leg.EdgeDist' -KEY_OUT_SPTING_MIN_EDGE_DIST = 'Cleat.Spting_leg.MinEdgeDist' -KEY_OUT_SPTING_MAX_EDGE_DIST = 'Cleat.Spting_leg.MaxEdgeDist' - - -KEY_OUT_DISP_PLATETHK_REP = 'Thickness (mm)' -KEY_OUT_PLATETHK = 'Plate.Thickness' -KEY_OUT_DISP_PLATETHK = 'Thickness (mm)' -KEY_OUT_PLATE_HEIGHT = 'Plate.Height' -KEY_OUT_DISP_PLATE_HEIGHT = 'Height (mm)' -KEY_OUT_DISP_PLATE_MIN_HEIGHT = 'Min.Height (mm)' - -KEY_OUT_INTER_PLATE_HEIGHT = 'Plate.InterHeight' -KEY_OUT_DISP_INTER_PLATE_HEIGHT = 'Height (mm)' - - -KEY_OUT_INTER_PLATE_LENGTH = 'Plate.InterLength' -KEY_OUT_DISP_INTER_PLATE_LENGTH = 'Length (mm)' - - -KEY_OUT_INTERCONNECTION = 'Intermittent.Connection' -KEY_OUT_DISP_INTERCONNECTION = 'Connection (nos)' - -KEY_OUT_INTERSPACING = 'Intermittent.Spacing' -KEY_OUT_DISP_INTERSPACING = 'Spacing (mm)' - - -KEY_OUT_PLATE_CAPACITY = 'Plate.Capacity' -KEY_OUT_PLATE_LENGTH = 'Plate.Length' -KEY_OUT_DISP_PLATE_LENGTH = 'Length (mm)' -KEY_OUT_DISP_PLATE_MIN_LENGTH = 'Min.Plate Length (mm)' -KEY_OUT_DISP_MEMB_MIN_LENGTH = 'Min.Member Length (mm)' - -KEY_OUT_PLATE_WIDTH = 'Plate.Width' -KEY_OUT_DISP_PLATE_WIDTH = 'Width (mm)' -c = 'Width (mm)' - -KEY_OUT_SEATED_ANGLE_DESIGNATION = "SeatedAngle.Designation" -KEY_OUT_DISP_ANGLE_DESIGNATION = "Designation" -KEY_OUT_SEATED_ANGLE_THICKNESS = "SeatedAngle.Thickness" -KEY_OUT_DISP_SEATED_ANGLE_THICKNESS = "Leg Thickness (mm)" -KEY_OUT_SEATED_ANGLE_LEGLENGTH = "SeatedAngle.LegLength" -KEY_OUT_DISP_SEATED_ANGLE_LEGLENGTH = "Leg Length (mm)" -KEY_OUT_SEATED_ANGLE_WIDTH = "SeatedAngle.Width" -KEY_OUT_DISP_ANGLE_WIDTH = "Width (mm)" -KEY_OUT_SEATED_ANGLE_BOLT_COL = "SeatedAngle.Bolt_Spacing_col" -KEY_OUT_DISP_SEATED_ANGLE_BOLT_COL = "Bolt Spacing Details" -KEY_OUT_SEATED_ANGLE_BOLT_BEAM = "SeatedAngle.Bolt_Spacing_beam" -KEY_OUT_DISP_SEATED_ANGLE_BOLT_BEAM = "Bolt Spacing Details" - -KEY_OUT_TOP_ANGLE_DESIGNATION = "TopAngle.Designation" -# KEY_OUT_DISP_TOP_ANGLE_DESIGNATION = "Designation" -KEY_OUT_TOP_ANGLE_WIDTH = "TopAngle.Width" -# KEY_OUT_DISP_TOP_ANGLE_WIDTH = "Width (mm)" -KEY_OUT_TOP_ANGLE_BOLT_COL = "TopAngle.Bolt_Spacing_col" -KEY_OUT_DISP_TOP_ANGLE_BOLT_COL = "Bolt Spacing Details" -KEY_OUT_TOP_ANGLE_BOLT_BEAM = "TopAngle.Bolt_Spacing_beam" -KEY_OUT_DISP_TOP_ANGLE_BOLT_BEAM = "Bolt Spacing Details" - -KEY_OUT_PLATE_SHEAR_DEMAND = 'Plate.ShearDemand' -KEY_OUT_DISP_PLATE_SHEAR_DEMAND = 'Shear Demand (kN)' -KEY_OUT_PLATE_SHEAR = 'Plate.Shear' -KEY_OUT_DISP_PLATE_SHEAR = 'Shear Yielding Capacity (kN)' -KEY_OUT_PLATE_YIELD = 'Plate.Yield' -KEY_OUT_DISP_PLATE_YIELD = 'Yield Capacity' -KEY_OUT_PLATE_RUPTURE = 'Plate.Rupture' -KEY_OUT_DISP_PLATE_RUPTURE = 'Rupture Capacity (kN)' - -KEY_OUT_PLATE_BLK_SHEAR = 'Plate.BlockShear' -KEY_OUT_DISP_PLATE_BLK_SHEAR = 'Block Shear Capacity (kN)' -KEY_OUT_PLATE_MOM_DEMAND = 'Plate.MomDemand' -KEY_OUT_DISP_PLATE_MOM_DEMAND = 'Moment Demand (kNm)' -KEY_OUT_DISP_PLATE_MOM_DEMAND_SEP = 'Moment Demand per Bolt (kNm)' -KEY_OUT_PLATE_MOM_CAPACITY = 'Plate.MomCapacity' -KEY_OUT_DISP_PLATE_MOM_CAPACITY = 'Moment Capacity (kNm)' -KEY_OUT_DISP_PLATE_MOM_CAPACITY_SEP = 'Moment Capacity per Bolt (kNm)' -KEY_OUT_EP_MOM_CAPACITY = 'Plate.MomentCapacity' -KEY_OUT_DISP_EP_MOM_CAPACITY = 'Moment Capacity (kNm)' - -KEY_OUT_PLATE_TENSION = 'Plate.TensionYield' - -KEY_OUT_DISP_PLATE_TENSION = 'Tension Yielding Capacity (kN)' - -KEY_OUT_PLATE_TENSION_RUP = 'Plate.TensionRupture' -KEY_OUT_DISP_PLATE_TENSION_RUP = 'Tension Rupture Capacity (kN)' - -KEY_OUT_PLATE_BLK_SHEAR_AXIAL = 'Plate.BlockShearAxial' -KEY_OUT_DISP_PLATE_BLK_SHEAR_AXIAL = 'Axial Block Shear Capacity (kN)' - -KEY_OUT_PLATE_CAPACITIES = 'capacities' -KEY_OUT_DISP_PLATE_CAPACITIES = 'Capacity' - -KEY_OUT_WELD_SIZE = 'Weld.Size' -KEY_OUT_DISP_WELD_SIZE = 'Size (mm)' - -KEY_OUT_INTER_WELD_SIZE = 'InterWeld.Size' -KEY_OUT_DISP_INTER_WELD_SIZE = 'Size (mm)' - -KEY_OUT_WELD_SIZE_FLANGE = 'Weld.Size_flange' -KEY_OUT_DISP_WELD_SIZE_FLANGE = 'Size at Flange (mm)' -KEY_OUT_WELD_SIZE_WEB = 'Weld.Size_web' -KEY_OUT_DISP_WELD_SIZE_WEB = 'Size at Web (mm)' -KEY_OUT_WELD_SIZE_STIFFENER = 'Weld.Size_stiffener' -KEY_OUT_DISP_WELD_SIZE_STIFFENER = 'Size at Stiffener (mm)' -KEY_OUT_DISP_WELD_SIZE_STIFFENER1 = 'Weld Size at Stiffener (mm)' -KEY_OUT_WELD_STRENGTH = 'Weld.Strength' -KEY_OUT_DISP_WELD_STRENGTH = 'Strength (N/mm)' -KEY_OUT_WELD_STRESS = 'Weld.Stress' -KEY_OUT_DISP_WELD_STRESS = 'Stress (N/mm)' -KEY_OUT_WELD_LENGTH = 'Weld.Length' -KEY_OUT_DISP_WELD_LENGTH = 'Length (mm)' -KEY_OUT_WELD_LENGTH_EFF = 'Weld.EffLength' -KEY_OUT_DISP_WELD_LENGTH_EFF = 'Eff.Length (mm)' - -KEY_OUT_DISP_MEMB_TEN_YIELD = 'Tension Yield Capacity (KN)' -KEY_OUT_DISP_MEMB_TEN_RUPTURE = 'Tension Rupture Capacity' -KEY_OUT_DISP_MEMB_BLK_SHEAR = 'Block Shear Capacity' - - -KEY_OUT_NO_BOLTS_FLANGE = 'ColumnEndPlate.nbf' -KEY_OUT_NO_BOLTS_FLANGE_TOTAL = 'ColumnEndPlate.nbftotal' -KEY_OUT_DISP_NO_BOLTS_FLANGE = 'No. of Bolts (along one side of the flange overhang) (n)' -KEY_OUT_DISP_NO_BOLTS_FLANGE_TOTAL = 'No. of Bolts (along flange)' -KEY_OUT_NO_BOLTS_WEB = 'ColumnEndPlate.nbw' -KEY_OUT_NO_BOLTS_WEB_TOTAL = 'ColumnEndPlate.nbwtotal' - -KEY_OUT_DISP_NO_BOLTS_WEB = 'No. of Bolts (along one side of the web) (n)' -KEY_OUT_DISP_NO_BOLTS_WEB_TOTAL = 'No. of Bolts (along web)' - -KEY_OUT_NO_BOLTS = 'ColumnEndPlate.nb' -KEY_OUT_DISP_NO_BOLTS = 'Total No. of Bolts' -KEY_PITCH_2_FLANGE = 'ColumnEndPlate.p2_flange' -KEY_DISP_PITCH_2_FLANGE = 'Pitch2 along Flange' -KEY_PITCH_2_WEB = 'ColumnEndPlate.p2_web' -KEY_DISP_PITCH_2_WEB = 'Pitch2 along Web' - -KEY_PITCH_2_FLANGE1 = 'ColumnEndPlate.p2_flange' -KEY_DISP_PITCH_2_FLANGE1 = 'Pitch (bolts along centre) (p2)' -KEY_PITCH_2_WEB1 = 'ColumnEndPlate.p2_web' -KEY_DISP_PITCH_2_WEB1 = 'Pitch along centre bolt (p2)' -KEY_BOLT_FLANGE_SPACING = 'Bolt.flange_bolts' -KEY_DISP_BOLT_FLANGE_SPACING = 'Flange Bolts Spacing' -KEY_BOLT_WEB_SPACING = 'Bolt.web_bolts' -KEY_DISP_BOLT_WEB_SPACING = 'Web Bolts Spacing' - - - -KEY_CONN_PREFERENCE = 'plate.design_method' -KEY_DISP_CONN_PREFERENCE = 'Design Method' -VALUES_CONN_PREFERENCE = ["Select","Plate Oriented", "Bolt Oriented"] -KEY_OUT_STIFFENER_HEIGHT = 'Stiffener.height' -KEY_OUT_DISP_STIFFENER_HEIGHT = 'Stiffener Height' -KEY_OUT_STIFFENER_WIDTH = 'Stiffener.width' -KEY_OUT_DISP_STIFFENER_WIDTH = 'Stiffener Width' -KEY_OUT_STIFFENER_THICKNESS = 'Stiffener.thickness' -KEY_OUT_DISP_STIFFENER_THICKNESS = 'Stiffener Thickness' -KEY_OUT_WELD_TYPE = 'Stiffener.weld' -KEY_OUT_WELD_TYPE1 = 'Stiffener.weld_flange' -KEY_OUT_DISP_WELD_TYPE = 'Weld Between Stiffener and Column flange' -KEY_OUT_DISP_WELD_TYPE1 = 'Weld Between Stiffener and End plate' -KEY_OUT_STIFFENER_DETAILS = 'Stiffener.Details' -KEY_OUT_STIFFENER_SKETCH = 'Stiffener.Sketch' -KEY_OUT_BP_TYPICAL_SKETCH = 'BasePlate.Sketch' -KEY_OUT_BP_TYPICAL_DETAILING = 'BasePlate.Detailing' -KEY_OUT_DISP_BP_DETAILING = 'Typical Detailing' -KEY_OUT_DISP_BP_DETAILING_SKETCH = 'Detailing' -KEY_OUT_CONTINUITY_DETAILS = 'ContinuityPlate.Details' -KEY_OUT_COL_WEB_STIFFENER_DETAILS = 'ColWebStiffenerPlate.Details' -KEY_OUT_DISP_STIFFENER_DETAILS = 'Stiffener Plate' -KEY_OUT_DISP_STIFFENER_DIMENSIONS = 'Dimensions' -KEY_OUT_DISP_STIFFENER_SKETCH = 'Typical Sketch' -KEY_OUT_DISP_CONTINUITY_PLATE_DETAILS = 'Continuity Plate' -KEY_OUT_DISP_WEB_STIFFENER_PLATE_DETAILS = 'Web Stiffener Plate' -KEY_OUT_STIFFENER_TITLE = 'Stiffener.Title' -KEY_P2_WEB = 'Bolt.pitch2_web' -KEY_P2_FLANGE = 'Bolt.pitch2_flange' -KEY_Y_SQR = 'Bolt.y_sqr' -KEY_BOLT_TENSION = 'Bolt.t_b' -KEY_BOLT_SHEAR = 'Bolt.v_sb' -KEY_PLATE_MOMENT = 'Plate.m_ep' -KEY_OUT_STIFFENER_LENGTH = 'Stiffener.Length' -KEY_OUT_STIFFENER_LENGTH_CHS = 'Stiffener.Length' -KEY_OUT_CONTINUITY_PLATE_NOS = 'ContinuityPlate.Number' -KEY_OUT_CONTINUITY_PLATE_LENGTH = 'ContinuityPlate.Length' -KEY_OUT_CONTINUITY_PLATE_WIDTH = 'ContinuityPlate.Width' -KEY_OUT_CONTINUITY_PLATE_THK = 'ContinuityPlate.Thickness' -KEY_OUT_WEB_STIFFENER_PLATE_NOS = 'WebStiffener.Number' -KEY_OUT_WEB_STIFFENER_PLATE_LENGTH = 'WebStiffener.Length' -KEY_OUT_WEB_STIFFENER_PLATE_WIDTH = 'WebStiffener.Width' -KEY_OUT_WEB_STIFFENER_PLATE_THK = 'WebStiffener.Thickness' -KEY_OUT_DISP_STIFFENER_LENGTH = 'Length (mm)' -KEY_OUT_DISP_CONTINUITY_PLATE_NUMBER = 'Number of Continuity Plate(s)' -KEY_OUT_DISP_WEB_STIFFENER_PLATE_NUMBER = 'Number of Stiffener(s)' -KEY_OUT_DISP_CONTINUITY_PLATE_LENGTH = 'Length (mm)' -KEY_OUT_DISP_WEB_PLATE_PLATE_DEPTH = 'Depth (mm)' -KEY_OUT_DISP_CONTINUITY_PLATE_WIDTH = 'Width (mm)' -KEY_OUT_DISP_CONTINUITY_PLATE_THK = 'Thickness (mm)' -KEY_OUT_STIFFENER_HEIGHT = 'Stiffener.Height' -KEY_OUT_STIFFENER_HEIGHT_CHS = 'Stiffener.Height' -KEY_OUT_STIFFENER_WIDTH = 'Stiffener.Width' -KEY_OUT_DISP_STIFFENER_HEIGHT = 'Height (mm)' -KEY_OUT_DISP_STIFFENER_WIDTH = 'Width (mm)' -KEY_OUT_STIFFENER_THICKNESS = 'Stiffener.Thickness' -KEY_OUT_STIFFENER_THICKNESS_CHS = 'Stiffener.Thickness' -KEY_OUT_DISP_STIFFENER_THICKNESS = 'Thickness (mm)' - -KEY_OUT_DISP_LOCAL_WEB_YIELDING = 'Local Web Yielding' -KEY_OUT_DISP_COMP_BUCKLING_WEB = 'Compression Buckling of Web' -KEY_OUT_DISP_WEB_CRIPPLING = 'Web Crippling' -KEY_OUT_DISP_COMP_STRENGTH = 'Compression Strength (kN)' -#Continuity Plate -KEY_OUT_DISP_CONT_PLATE_REQ = 'Continuity Plate Required?' -KEY_OUT_DISP_DIAG_PLATE_REQ = 'Web Stiffener Plate Required?' -KEY_OUT_DISP_AREA_REQ= "Area Required (mm2)" -KEY_OUT_DISP_NOTCH_SIZE ="Notch Size (mm)" -KEY_OUT_DISP_DIAG_LOAD_STIFF="Load taken by Stiffener" -KEY_OUT_DISP_DIAGONAL_PLATE_DEPTH = 'Depth (mm)' -KEY_OUT_DISP_DIAGONAL_PLATE_WIDTH = 'Width (mm)' -# KEY_OUT_DISP_WEB_PLATE_CONT_T - - - -KEY_OUT_WELD_DETAILS = 'Weld.Details' -DISP_TITLE_WELD = 'Weld' -DISP_TITLE_WELD_FLANGE = 'Weld at Flange' -DISP_TITLE_WELD_TYPICAL_DETAIL = 'Typical Sketch' -DISP_TITLE_WELD_WEB = 'Weld at Web' -KEY_OUT_WELD_SIZE = 'Weld.Size' -KEY_OUT_WELD_DETAILS = 'Weld.Details' -KEY_OUT_WELD_TYPE = 'Weld.Type' -KEY_OUT_DISP_WELD_SIZE = 'Size (mm)' -KEY_OUT_DISP_WELD_SIZE_EP = 'Size (mm)' -KEY_OUT_DISP_WELD_TYPE = 'Type' -KEY_OUT_WELD_STRENGTH = 'Weld.Strength' -KEY_OUT_DISP_WELD_STRENGTH = 'Strength (N/mm2)' - -KEY_OUT_WELD_STRESS = 'Weld.Stress' -KEY_OUT_WELD_STRESS_NORMAL = 'Weld.NormalStress' -KEY_OUT_WELD_STRESS_SHEAR = 'Weld.ShearStress' -KEY_OUT_WELD_STRESS_COMBINED = 'Weld.StressCombined' -KEY_OUT_DISP_WELD_STRESS_COMBINED = 'Combined Stress (N/mm2)' -KEY_OUT_DISP_WELD_STRESS_EQUIVALENT = 'Equivalent Stress (N/mm2)' -KEY_OUT_DISP_WELD_STRESS = 'Stress (N/mm)' -KEY_OUT_DISP_WELD_NORMAL_STRESS = 'Normal Stress (N/mm2)' -KEY_OUT_DISP_WELD_SHEAR_STRESS = 'Shear Stress (N/mm2)' -KEY_OUT_DISP_WELD_STRESS_AXIAL = 'Weld.Stress due to axial force' -KEY_OUT_DISP_WELD_STRESS_SHEAR = 'Weld.Stress due to shear force' -KEY_OUT_DISP_WEB_WELD_LENGTH = 'Web Weld Length (mm)' -KEY_OUT_WELD_LENGTH = 'Weld.Length' -KEY_OUT_DISP_WELD_LENGTH = 'Total Length (mm)' -KEY_OUT_WELD_LENGTH_EFF = 'Weld.EffLength' -KEY_OUT_DISP_WELD_LENGTH_EFF = 'Eff.Length (mm)' -KEY_OUT_WELD_STRENGTH_RED = 'Weld.Strength_red' -KEY_OUT_DISP_WELD_STRENGTH_RED = 'Red.Strength (N/mm)' - -DISP_OUT_TITLE_SPTDLEG = "Bolts on Supported Leg" -DISP_OUT_TITLE_SPTINGLEG = "Bolts on Supporting Leg" -DISP_OUT_TITLE_CLEAT = "Cleat Angle" -KEY_OUT_CLEAT_SECTION = "Cleat.Angle" -KEY_OUT_DISP_CLEAT_SECTION = "Cleat Angle Designation" -KEY_OUT_CLEATTHK = 'Plate.Thickness' -KEY_OUT_DISP_CLEATTHK = 'Thickness (mm)' -KEY_OUT_CLEAT_HEIGHT = 'Plate.Height' -KEY_OUT_DISP_CLEAT_HEIGHT = 'Height (mm)' -KEY_OUT_CLEAT_SPTDLEG = 'Cleat.SupportedLength' -KEY_OUT_DISP_CLEAT_SPTDLEG = 'Length (mm)' -KEY_OUT_CLEAT_SPTINGLEG = 'Cleat.SupportingLength' -KEY_OUT_DISP_CLEAT_SPTINGLEG = 'Length (mm)' - -KEY_OUT_CLEAT_SHEAR = 'Cleat.Shear' -KEY_OUT_DISP_CLEAT_SHEAR = 'Shear ' -KEY_OUT_CLEAT_BLK_SHEAR = 'Cleat.BlockShear' - -KEY_OUT_CLEAT_MOM_DEMAND = 'Cleat.MomDemand' - -KEY_OUT_CLEAT_MOM_CAPACITY = 'Cleat.MomCapacity' - - - -KEY_DISP_SEC_PROFILE = 'Section Profile*' -VALUES_SEC_PROFILE = ['Beams', 'Columns', 'RHS', 'SHS', 'CHS'] -VALUES_SEC_PROFILE_2 = ['Angles', 'Back to Back Angles', 'Star Angles', 'Channels', 'Back to Back Channels'] - -KEY_LENZZ = 'Member.Length_zz' -KEY_DISP_LENZZ = 'Length (z-z)' - - -KEY_LENYY = 'Member.Length_yy' -KEY_DISP_LENYY = 'Length (y-y)' - -DISP_TITLE_SC = 'Supporting Condition' - -KEY_END1 = 'End_1' -KEY_DISP_END1 = 'End 1' -VALUES_END1 = ['Fixed', 'Free', 'Hinged', 'Roller'] - - -KEY_END2 = 'End_2' -KEY_DISP_END2 = 'End 2' -VALUES_END2 = ['Fixed', 'Free', 'Hinged', 'Roller'] - -KEY_END_CONDITION = 'End Condition' -KEY_DISP_END_CONDITION = 'End Condition' -DISP_TITLE_CLEAT = 'Cleat Angle' -DISP_TITLE_ANGLE = 'Angle Section' -DISP_TITLE_CHANNEL = 'Channel Section' -KEY_CLEATHT='CleatHt' -KEY_DISP_CLEATHT='Height(mm)' -KEY_DISP_CLEATSEC='Cleat Section *' -KEY_DISP_SEATEDANGLE = 'Seated Angle *' -KEY_DISP_TOPANGLE = 'Top Angle *' -#Design Report Strings -DISP_NUM_OF_BOLTS = 'No. of Bolts' -DISP_NUM_OF_ROWS = 'No. of Bolt Rows' -DISP_NUM_OF_COLUMNS = 'No. of Bolt Columns' -DISP_TITLE_COMPMEM='Compression member' -KEY_SECTYPE = 'Section Type' -KEY_DISP_SECTYPE = 'Section Type*' -KEY_DISP_SECSIZE = 'Section Size*' -KEY_DISP_SECSIZE_REPORT = 'Section Size' -KEY_LENMEM = 'Length of Member' -KEY_DISP_LENMEM = 'Length of Member' -DISP_TITLE_FL = 'Factored loads' -KEY_AXFOR = 'Axial Force' -KEY_DISP_AXFOR = 'Axial Force (kN)*' -KEY_PLTHK = 'Plate thk' -KEY_DISP_PLTHK = 'Plate thk (mm)' -KEY_PLTHICK = 'Plate thk' -KEY_DISP_PLTHICK = 'Plate Thickness (mm)' -KEY_DISP_PLATE_THICK = 'Plate Thickness (mm)' -KEY_DIAM = 'Diameter' -KEY_DISP_DIAM = 'Diameter (mm)' -KEY_NOROWS = 'No of Rows of Bolts' -KEY_DISP_NOROWS = 'No of Rows of Bolts' -KEY_NOCOLS = 'No of Column of Bolts' -KEY_DISP_NOCOLS = 'No of Column of Bolts' -KEY_ROWPI = 'Row Pitch' -KEY_DISP_ROWPI = 'Row Pitch' -KEY_COLPI = 'Column Pitch' -KEY_DISP_COLPI = 'Column Pitch' -KEY_ENDDIST = 'End Distance' -KEY_DISP_ENDDIST = 'End Distance' -KEY_EDGEDIST = 'Edge Distance' -KEY_DISP_EDGEDIST = 'Edge Distance' -KEY_CONNLOC = 'Conn Location' -KEY_DISP_CONNLOC = 'Conn Location' -KEY_LEN_INLINE = 'Total length in line with tension' -KEY_DISP_LEN_INLINE = 'Total Length in line with tension' -KEY_LEN_OPPLINE = 'Total length opp line with tension' -KEY_DISP_LEN_OPPLINE = 'Total Length opp line with tension' - - -VALUES_ANGLESEC_CUSTOMIZED= connectdb("Angles", call_type="popup") - -def get_available_cleat_list(input_angle_list, max_leg_length=math.inf, min_leg_length=0.0, position="outer"): - - available_angles = [] - for designation in input_angle_list: - leg_a_length,leg_b_length,t,r_r = get_leg_lengths(designation) - if position == "inner": - min_leg_length_outer = min_leg_length + t + r_r - max_leg_length_outer = max_leg_length + t + r_r - else: - min_leg_length_outer = min_leg_length - max_leg_length_outer = max_leg_length - - # print(min_leg_length,max_leg_length) - if operator.le(max(leg_a_length,leg_b_length),max_leg_length_outer) and operator.ge(min(leg_a_length,leg_b_length), min_leg_length_outer) and leg_a_length==leg_b_length: - # print("appended", designation) - available_angles.append(designation) - # else: - # print("popped",designation) - return available_angles - - -def get_leg_lengths(designation): - - """ - Function to fetch designation values from respective Tables. - """ - conn = sqlite3.connect(PATH_TO_DATABASE) - db_query = "SELECT a, b, t, R1 FROM Angles WHERE Designation = ?" - cur = conn.cursor() - cur.execute(db_query, (designation,)) - row = cur.fetchone() - - a = row[0] - b = row[1] - t = row[2] - r_r = row[3] - # axb = axb.lower() - leg_a_length = float(a) - leg_b_length = float(b) - conn.close() - return leg_a_length,leg_b_length,t,r_r - -all_angles = connectdb("Angles","popup") -VALUES_CLEAT_CUSTOMIZED = get_available_cleat_list(all_angles, 200.0, 50.0) -print(all_angles) -print("customised") -print(VALUES_CLEAT_CUSTOMIZED) - -BOLT_DESCRIPTION = str("\n" - "\n" - "\n" - "\n" - "
\n" - "

IS 800 Table 20 Typical Average Values for Coefficient of Friction (µf)

\n" - "


\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "
\n" - "

Treatment of Surfaces

\n" - "

µ_f

\n" - "

i)

\n" - "

Surfaces not treated

\n" - "

0.2

\n" - "

ii)

\n" - "

Surfaces blasted with short or grit with any loose rust removed, no pitting

\n" - "

0.5

\n" - "

iii)

\n" - "

Surfaces blasted with short or grit and hot-dip galvanized

\n" - "

0.1

\n" - "

iv)

\n" - "

Surfaces blasted with short or grit and spray - metallized with zinc (thickness 50-70 µm)

\n" - "

0.25

\n" - "

v)

\n" - "

Surfaces blasted with shot or grit and painted with ethylzinc silicate coat (thickness 30-60 µm)

\n" - "

0.3

\n" - "

vi)

\n" - "

Sand blasted surface, after light rusting

\n" - "

0.52

\n" - "

vii)

\n" - "

Surfaces blasted with shot or grit and painted with ethylzinc silicate coat (thickness 60-80 µm)

\n" - "

0.3

\n" - "

viii)

\n" - "

Surfaces blasted with shot or grit and painted with alcalizinc silicate coat (thickness 60-80 µm)

\n" - "

0.3

\n" - "

ix)

\n" - "

Surfaces blasted with shot or grit and spray metallized with aluminium (thickness >50 µm)

\n" - "

0.5

\n" - "

x)

\n" - "

Clean mill scale

\n" - "

0.33

\n" - "

xi)

\n" - "

Sand blasted surface

\n" - "

0.48

\n" - "

xii)

\n" - "

Red lead painted surface

\n" - "

0.1

\n" - "


") - -WELD_DESCRIPTION = str("\n" - "\n" - "

Shop weld takes a material safety factor of 1.25

\n" - "

Field weld takes a material safety factor of 1.5

\n" - "

(IS 800 - cl. 5. 4. 1 or Table 5)

") - -# DETAILING_DESCRIPTION = str("\n" -# "\n" -# "

The minimum edge and end distances from the centre of any hole to the nearest edge of a plate shall not be less than 1.7 times the hole diameter in case of [sheared or hand flame cut edges] and 1.5 times the hole diameter in case of [Rolled, machine-flame cut, sawn and planed edges] (IS 800 - cl. 10. 2. 4. 2)

\n" -# "


\n" -# "

This gap should include the tolerance value of 5mm. So if the assumed clearance is 5mm, then the gap should be = 10mm (= 5mm {clearance} + 5 mm{tolerance})

\n" -# "


\n" -# "

Specifying whether the members are exposed to corrosive influences, here, only affects the calculation of the maximum edge distance as per cl. 10.2.4.3

\n" -# "


") - - - -DETAILING_DESCRIPTION = str("\n" - "\n" - "

The minimum edge and end distances from the centre of any hole to the nearest edge of a plate shall not be less than 1.7 times the hole diameter in case of [sheared or hand flame cut edges] and 1.5 times the hole diameter in case of [Rolled, machine-flame cut, sawn and planed edges] (IS 800 - cl. 10. 2. 4. 2)

\n" - "


\n" - "

This gap should include the tolerance value of 5mm or 1.5mm. So if the assumed clearance is 5mm, then the gap should be = 10mm (= 5mm {clearance} + 5mm {tolerance} or if the assumed clearance is 1.5mm, then the gap should be = 3mm (= 1.5mm {clearance} + 1.5mm {tolerance}. These are the default gap values based on the site practice for convenience of erection and IS 7215,Clause 2.3.1. The gap value can also be zero based on the nature of connection where clearance is not required.

\n" - "


\n" - "

Specifying whether the members are exposed to corrosive influences, here, only affects the calculation of the maximum edge distance as per cl. 10.2.4.3

\n" - "


") diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..b7436c923 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +FROM ubuntu:22.04 + + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Asia/Kolkata + +RUN apt update && \ + apt install -y wget bzip2 software-properties-common curl python3-pip libpq-dev libssl-dev build-essential libgl1-mesa-glx libglu1-mesa freecad texlive-full && \ + mkdir -p /snap/bin && \ + ln -s /usr/bin/freecad /snap/bin/freecad.cmd + + +RUN mkdir -p /opt/conda && \ + wget https://repo.anaconda.com/miniconda/Miniconda3-py37_4.8.2-Linux-x86_64.sh -O /opt/conda/miniconda.sh && \ + bash /opt/conda/miniconda.sh -b -p /opt/miniconda && \ + rm -f /opt/conda/miniconda.sh && \ + /opt/miniconda/bin/conda init bash + +COPY install_dependencies.sh /app/install_dependencies.sh +COPY ./conda_packages/ /app/conda_packages/ + +RUN bash -c "source /opt/miniconda/etc/profile.d/conda.sh && \ + conda create -n myenv python=3.7.6 -y && \ + conda activate myenv && \ + pip install --upgrade pip pyopenssl && \ + pip install -r requirements.txt" + # pip install /app/conda_packages/pdflatex-0.1.3.tar.gz \ + # /app/conda_packages/PyLaTeX-1.3.1.tar.gz \ + # /app/conda_packages/XlsxWriter-1.2.8.tar.gz \ + # /app/conda_packages/Pygments-2.6.1.tar.gz \ + # /app/conda_packages/openpyxl-3.0.3.tar.gz \ + # /app/conda_packages/PyYAML-5.3.1.tar.gz \ + # /app/conda_packages/PyQt5-5.14.2-5.14.2-cp35.cp36.cp37.cp38-abi3-manylinux2014_x86_64.whl \ + # /app/conda_packages/pdfkit-0.6.1-py3-none-any.whl \ + # /app/conda_packages/pandas-1.0.5-cp37-cp37m-manylinux1_x86_64.whl \ + # /app/conda_packages/pynput-1.6.8-py2.py3-none-any.whl \ + # /app/conda_packages/PyGithub-1.54.1.tar.gz" + +WORKDIR /app + +RUN chmod +x /app/install_dependencies.sh && \ + bash -c "source /opt/miniconda/etc/profile.d/conda.sh && \ + conda activate myenv && \ + /app/install_dependencies.sh" + +COPY . /app + +RUN bash -c "source /opt/miniconda/etc/profile.d/conda.sh && \ + conda activate myenv && \ + pip install -r requirements.txt" + +ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6 +EXPOSE 8000 + +CMD ["bash", "-c", "source /opt/miniconda/etc/profile.d/conda.sh && conda activate myenv && python manage.py runserver 0.0.0.0:8000"] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 71e218090..000000000 --- a/LICENSE +++ /dev/null @@ -1,285 +0,0 @@ -Copyright (C) 2017 Osdag -OsdagⓇ and the Osdag logo are the registered trademarks of the Indian Institute of Technology Bombay (IIT Bombay) -======================================================================================================== -Osdag is a cross-platform free/libre and open-source software for the design (and detailing) of steel structures, following the Indian Standard IS 800:2007. It allows the user to design steel connections, members and systems using a graphical user interface. The interactive GUI provides a 3D visualisation of the designed component and an option to export the CAD model to any drafting software for the creation of construction/fabrication drawings. The design is typically optimised following industry best practices. -Starting with version 2017.06.a.e2dd, the beta version of Osdag is released under the terms and conditions of the GNU LESSER GENERAL PUBLIC LICENSE (LGPL) Version 3. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see . - -Notice of Third Party Software Licenses: -Osdag contains open source software packages from third parties. These are available on an “as is” basis and subject to their individual license agreements. These licenses are available in License-dependencies.txt. These third party tools are subject to their individual licenses as well as the Osdag license. - -======================================================================================================== - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. - -======================================= -Osdag development team (2014 - Present) -======================================= -Project Investigator - -Professor Siddhartha Ghosh - -=============================== -Research Associates/Assistants - -Mr. Danish Ansari -Mr. Yash Lokhande -Mr. Anand Swaroop -Mr. Darshan Divesan -Mr. Anjali Jatav -Mr. Sourabh Das -Ms. Deepthi Reddy -Mr. Ajmal Babu MS -Mr. N Dharma Teja -Ms. Thushara Pushkaran -Ms. Reshma Konjari -Ms. Swathi M -Mr. Siddesh Chavan -Ms. Deepa Chaudhari -Mr. Jayant Patil -Mr. Suhel Hashmi -Mr. Subhrajit Dutta -Mr. Shihabuddin Khan - -=============================== -Project Interns - -Mr.Aravind P -Mr. Sasir Pentyala -Mr. Riyaz Khan -Mr. Mayur Mistry -Mr. Yash Lokhande -Mr. Aryamaan Pandey -Ms. Saumya Mishra -Ms. Pragya Thakur -Mr. Tanmay Kalla -Mr. Nikhil Kapoor -Mr. Himanshu Singh -Mr. Aditya Pawar -Ms. Shreya Bhende -Ms. Deepthi Reddy -Mr. Ansari Mohammad Umair -Mr. Amir Chappalwala -Mr. Zunzunia Arsil -Mr. Mohammad Azhar U Din Mir -Mr. Satyam Singh Niranjan -Mr. Anshul Kumar Singh -Mr. Mosam Patel -Mr. Shahadad PP -Ms. Priti Kumari - -=============================== -Project Management - -Ms. Usha Viswanathan -Ms. Vineeta Parmar -Mr. Sunil Shetye -Mr. Kiran Kishore - -=============================== -Web, Graphics, Promotions and System Administrators Team - -Mr. Prashant Sinalkar -Ms. Dipti Ghosalkar -Ms. Sashi Rekha B M K -Ms. Priyanka Bhagwat -Mr. Lee Thomas Stephen -Mr. Rohan Mhatre -Mr. Khushal Singh Rajput -Mr. Yash Vohra -Ms. Ulfa Khawaja - -=============================== -Office Staff - -Ms.Komal Solanki -Mr.Vishal Birare -Mr. Sushant Bammkanti -Ms. Priya Hiregange - -=============================== -Osdag Expert Reviewers - -Professor S R Satish Kumar (IIT Madras, Chennai) -Dr. Pradyumna M (Independent Consultant, Bengaluru) -Mr. Yogesh D Pisal (Aker Powergas Pvt Ltd, Mumbai) -Professor Meera Raghunandan (IIT Bombay, Mumbai) -Mr. Somnath Mukherjee (MN Dastur & Co (P) Ltd, Kolkata) -Mr. Manas M Ghosh (INSDAG, Kolkata) -Mr. Pratip Bhattacharya (Tata Consulting Engineers Ltd, Kolkata) -Dr. Harshvardhan Subbarao (Construma Consultancy Pvt Ltd, Mumbai) -Professor V Kalyanaraman (Retd.) (IIT Madras, Chennai) \ No newline at end of file diff --git a/Module_test.py b/Module_test.py deleted file mode 100644 index d7a757d22..000000000 --- a/Module_test.py +++ /dev/null @@ -1,314 +0,0 @@ -import os -import errno -import yaml -import sys -import unittest -from pathlib import Path -import ast -import logging -is_travis = 'TRAVIS' in os.environ -############################ Pre-Build Database Updation/Creation ################# -sqlpath = Path('ResourceFiles/Database/Intg_osdag.sql') -sqlitepath = Path('ResourceFiles/Database/Intg_osdag.sqlite') - -if sqlpath.exists(): - if not sqlitepath.exists(): - cmd = 'sqlite3 ' + str(sqlitepath) + ' < ' + str(sqlpath) - os.system(cmd) - sqlpath.touch() - print('Database Created') - - elif sqlitepath.stat().st_size == 0 or sqlitepath.stat().st_mtime < sqlpath.stat().st_mtime - 1: - try: - sqlitenewpath = Path('ResourceFiles/Database/Intg_osdag_new.sqlite') - cmd = 'sqlite3 ' + str(sqlitenewpath) + ' < ' + str(sqlpath) - error = os.system(cmd) - print(error) - # if error != 0: - # raise Exception('SQL to SQLite conversion error 1') - # if sqlitenewpath.stat().st_size == 0: - # raise Exception('SQL to SQLite conversion error 2') - os.remove(sqlitepath) - sqlitenewpath.rename(sqlitepath) - sqlpath.touch() - print('Database Updated', sqlpath.stat().st_mtime, sqlitepath.stat().st_mtime) - except Exception as e: - sqlitenewpath.unlink() - print('Error: ', e) - -######################################################################################### -from design_type.connection.fin_plate_connection import FinPlateConnection -from design_type.connection.cleat_angle_connection import CleatAngleConnection -from design_type.connection.seated_angle_connection import SeatedAngleConnection -from design_type.connection.end_plate_connection import EndPlateConnection -from design_type.connection.base_plate_connection import BasePlateConnection - -from design_type.connection.beam_cover_plate import BeamCoverPlate -from design_type.connection.beam_cover_plate_weld import BeamCoverPlateWeld -from design_type.connection.column_cover_plate_weld import ColumnCoverPlateWeld - -from design_type.tension_member.tension_bolted import Tension_bolted -from design_type.tension_member.tension_welded import Tension_welded -from design_type.connection.beam_beam_end_plate_splice import BeamBeamEndPlateSplice -from design_type.connection.beam_column_end_plate import BeamColumnEndPlate -from design_type.connection.column_cover_plate import ColumnCoverPlate -from design_type.connection.column_end_plate import ColumnEndPlate -from design_type.compression_member.compression import Compression -from Common import * - - -if not is_travis: - from cad.common_logic import CommonDesignLogic - from texlive .Design_wrapper import init_display - display, start_display, add_menu, add_function_to_menu = init_display(backend_str="qt-pyqt5") - - - - -all_modules = {'Base Plate':BasePlateConnection, 'Beam Coverplate Weld Connection':BeamCoverPlateWeld,'Beam Coverplate Connection':BeamCoverPlate, - 'Cleat Angle':CleatAngleConnection, 'Column Coverplate Weld Connection':ColumnCoverPlateWeld, 'Column Coverplate Connection':ColumnCoverPlate, - 'Column Endplate Connection':ColumnEndPlate, 'End Plate':EndPlateConnection, 'Fin Plate':FinPlateConnection,'Seated Angle': SeatedAngleConnection, - 'Tension Members Bolted Design':Tension_bolted, 'Tension Members Welded Design':Tension_welded, 'Compression Member':Compression, - } - - -''' -NOTE -> test cases are running on the modules available in available_module dict. - -Add more modules from all_modules dict to available_modules for testing or simply use all_modules dict if you want to run test for all modules. - -available_module dictionary is used in - - - 1. method 'runTest(self)' inside TestModules class. - 2. function 'suite()'. - -Make sure to make the necessary changes in above functions/methods if you are changing the name of available_module. -''' - -available_module = {KEY_DISP_FINPLATE:FinPlateConnection, KEY_DISP_ENDPLATE:EndPlateConnection, - KEY_DISP_SEATED_ANGLE:SeatedAngleConnection, KEY_DISP_CLEATANGLE:CleatAngleConnection, - KEY_DISP_BEAMCOVERPLATEWELD:BeamCoverPlateWeld,KEY_DISP_BEAMCOVERPLATE:BeamCoverPlate, - KEY_DISP_COLUMNCOVERPLATEWELD:ColumnCoverPlateWeld,KEY_DISP_COLUMNCOVERPLATE:ColumnCoverPlate, - KEY_DISP_TENSION_WELDED:Tension_welded,KEY_DISP_TENSION_BOLTED:Tension_bolted, - KEY_DISP_COLUMNENDPLATE:ColumnEndPlate,KEY_DISP_BASE_PLATE:BasePlateConnection, - KEY_DISP_BB_EP_SPLICE:BeamBeamEndPlateSplice, KEY_DISP_BCENDPLATE: BeamColumnEndPlate} - -#predefined pop-up summary. -popup_summary = {'ProfileSummary': {'CompanyName': 'LoremIpsum', 'CompanyLogo': '', 'Group/TeamName': - 'LoremIpsum', 'Designer': 'LoremIpsum'},'ProjectTitle': 'Fossee', 'Subtitle': '', 'JobNumber': '123', - 'AdditionalComments': 'No comments', 'Client': 'LoremIpsum'} - - - - -def make_sure_path_exists(path): # Works on all OS. - try: - os.makedirs(path) - except OSError as exception: - if exception.errno != errno.EEXIST: - raise - - - -input_file_path = os.path.join(os.path.dirname(__file__), 'ResourceFiles', 'design_example') # input folder path - -output_file_path = os.path.join(os.path.dirname(__file__), 'OUTPUT_FILES', 'Output_PDF') # output folder path - - - -make_sure_path_exists(output_file_path) #make sure output folder exists if not then create. - - - - - - -osi_files = [file for file in os.listdir(input_file_path) if file.endswith(".osi")] # get all osi files in input_file_path directory. - -files_data = [] # list of tuples in which the first item will be file name and second item will be data of that file in dictionary format. - -def precompute_data(): - - for file in osi_files: - - in_file = input_file_path + '/' + file - - with open(in_file, 'r') as fileObject: - - uiObj = yaml.load(fileObject, yaml.Loader) - - files_data.append((file, uiObj)) - - - -class Modules: - - def run_test(self,mainWindow,main,file_name, file_data): # FinPlate test function . Similarly make functions for other Modules. - - pdf_created = False - main.set_osdaglogger(None) - error = main.func_for_validation(main,file_data) # validating files and setting inputs (although we know files are valid). - - if error is None: # if ran successfully and all input values are set without any error. Now create pdf - - ''' - - - In save_design function second argument is popup summary which user gives as an input. - For testing purpose we are giving some default values for creating every pdf. - We are actually not comparing pdf. This is just for testing purpose whether function - is running fine and creating pdf or not. - - Some changes are made in save_design function. Instead of asking for output file - location from save_design function it'll ask from 'save_inputSummary' function inside - ui_summary_popup.py file immediately after getting popup inputs and send it to - save_design function using the same dictionary in which popup inputs are present - with key name as 'filename'. - - - ''' - file_name = file_name.split(".")[0] - - path = os.path.join(output_file_path, file_name) - - popup_summary['filename'] = path # adding this key in popup_summary dict. - - popup_summary['does_design_exist'] = False - - try: - - commLogicObj = CommonDesignLogic(display, ' ', main.module, main.mainmodule) - - status = main.design_status - - commLogicObj.call_3DModel(status, main) - - fName = os.path.join(os.path.dirname(__file__),'ResourceFiles','images','3d.png') - fName_front = os.path.join(os.path.dirname(__file__), 'ResourceFiles', 'images', 'front.png') - fName_side = os.path.join(os.path.dirname(__file__), 'ResourceFiles', 'images', 'side.png') - fName_top = os.path.join(os.path.dirname(__file__), 'ResourceFiles', 'images', 'top.png') - file_extension = fName.split(".")[-1] - - if file_extension == 'png': - - display.ExportToImage(fName) - display.View_Front() - display.FitAll() - display.ExportToImage(fName_front) - display.View_Top() - display.FitAll() - display.ExportToImage(fName_side) - display.View_Right() - display.FitAll() - display.ExportToImage(fName_top) - - display.EraseAll() - - popup_summary['does_design_exist'] = True - - except: - - pass - - with open(os.path.join(os.path.dirname(__file__),'logging_text.log'),'r') as LOG: # we are already creating this log file inside each module. - to_write = LOG.read() - - popup_summary['logger_messages'] = to_write - - main.save_design(main,popup_summary) # calling the function. - - pdf_created = True # if pdf created - - is_dict_same = True - ''' - path = os.path.join(os.path.dirname(__file__), 'OUTPUT_FILES', 'Command_line_output', file_name + ".txt") - - if os.path.isfile(path): - - with open(path,"r") as file_content: - - content = file_content.read() - - content = ast.literal_eval(content) # convert dictionary string to dictionary - - output_dict = main.results_to_test(main) - - if output_dict != content: - - is_dict_same = False # if dictionary is not equal. - ''' - - open(os.path.join(os.path.dirname(__file__),'logging_text.log'), 'w').close() # better to clear the file than removing the handlers. More efficient. - # Remove the log handlers also if you don't want to see all the accumulated messages repeating each time. - - return (pdf_created & is_dict_same) - - - -class TestModules(unittest.TestCase): - def __init__(self, input, output): - super(TestModules, self).__init__() - self.input = input - self.output = output - self.module = Modules() - - def runTest(self): - - file_name = self.input[0] - file_data = self.input[1] - file_class = available_module[file_data['Module']] # check the class. - ans = self.module.run_test(self.module, file_class,file_name, file_data) - self.assertTrue(ans is self.output) - - - - -def suite(): - - suite = unittest.TestSuite() - - ''' Make changes in this line to add files in TestSuite for testing according to your need or available modules. ''' - - suite.addTests(TestModules(item, True) for item in files_data if item[1]['Module'] in available_module) - - return suite - - - - -#Block print -def blockPrint(): - sys.stdout = open(os.devnull, 'w') - -# Restore print -def enablePrint(): - sys.stdout = sys.__stdout__ - - -if __name__ == '__main__': - - blockPrint() # disable printing to avoid printing from unnecessary print statments in each modules. Although log statements can still print. - precompute_data() # precompute all data. - - open(os.path.join(os.path.dirname(__file__),'logging_text.log'), 'w').close() # Clearing the log file for test module. - - log_file = "test_log_file.log" # log file in which test results will be written. - - - with open(log_file, 'w') as TEST_LOG_FILE: - result = unittest.TextTestRunner(stream = TEST_LOG_FILE,verbosity=2).run(suite()) # Writing results to log file. - - - with open(os.path.join(os.path.dirname(__file__),log_file), 'r') as content_file: - content = content_file.read() - - ''' - Reading the log file to see the output on console rather than opening the log file to see the output. - In actual test environment we won't need it. - ''' - enablePrint() # enable printing to print the test log. - print(content) - - - test_exit_code = int(not result.wasSuccessful()) - sys.exit(test_exit_code) # This step is important for travis CI if we want to show test case fail as build fail. diff --git a/README.md b/README.md deleted file mode 100644 index 2084a7f92..000000000 --- a/README.md +++ /dev/null @@ -1,298 +0,0 @@ -

-
- Open Steel Design and Graphics

-
Osdag

- Osdag is a cross-platform free/libre and open-source software for the design (and detailing) of steel structures, following the Indian Standard IS 800:2007. It allows the user to design steel connections, members and systems using a graphical user interface. The interactive GUI provides a 3D visualisation of the designed component and an option to export the CAD model to any drafting software for the creation of construction/fabrication drawings. The design is typically optimised following industry best practices. - Starting with version 2017.06.a.e2dd, the beta version of Osdag is released under the terms and conditions of the GNU LESSER GENERAL PUBLIC LICENSE (LGPL) Version 3. - -

- -## Table of contents -* Quick start -* Contributing -* Bugs and known issues -* Versioning -* Copyright and license - -## Quick start - -Download the latest version of Osdag - -### 1. Windows Installation - -#### System Requirements: - Supported Operating Systems: - Windows Vista - Windows 7, - Windows 8, - Windows 8.1, - Windows 10 - Supported Architecture: - 64-bit - RAM and Storage Space: - Minimum 2 Gb RAM recommended - Minimum 1 Gb free storage space recommended - - - Installation steps: - =================== - - Uninstalling Earlier Version of Osdag: If you have a previous version of Osdag installed then it is mandatory to uninstall the same. - - i) Go to the location where Osdag was installed and run "Uninstall.exe". - - # Note: If you have an active Antivirus package installed on your system please disable it during the installation of Osdag. Since, Osdag is not registered with the Microsoft store, the antivirus might block installation/running of Osdag. Osdag does not install any harmful package on your system. - - To install Osdag, Run Osdag_windows_setup.exe - - # Follow on-screen instructions AND select the following options in the Setup: - - i) Double click on the Osdag_windows_installer.exe to start the start the installation process. - ii) Click Next. - iii) Read the License and click 'I Agree' to proceed. - iv) Select the installation directory after checking the space requirement and click Next. - v) Click Install. - vi) Wait for the installation process to get over (this might take several minutes). - vii) Accept the MiKTeX license and click Next. - Viii) Choose the MiKTeX installation scope and click Next. - ix) Select the installation directory and click Next. - x) Keep the MiKTeX setting to default and click Next. - xi) Click start to install MiKTeX. - xii) Click Next. - Xiii) (optional) You can check for MiKTeX package updates. - xiv) Click Close to exit the MiKTeX setup wizard. - xv) The installation process will continue. After the process ends, click the Finish button. - - Osdag will be successfully installed! - - Running Osdag: - ============== - After the installation is complete, you may run Osdag by one of the following methods: - - i) Double-clicking on the Desktop shortcut or - ii) Press the Windows key and search Osdag - iii) Navigating to the installation-directory and double-clicking on the Osdag shortcut - - - -### 2. Ubuntu Installation - -#### System Requirements: - Operating System: - Ubuntu 14.04 (LTS) and later; 64-bit - Hardware Requirements: - Minimum 4 Gb RAM - Minimum of 1 Gb of free disk space - - This setup script is for machines running Ubuntu that do not have Miniconda3. - If you have Miniconda3 already installed on your computer, please skip Step/Command 1 and proceed to Step/Command 2. - - - Installation steps: - =================== - Extract the downloaded installer using the Archive Manager/File-Roller, or using the following command on the bash prompt: tar -xvf Osdag_ubuntu_installer.tar.gz - - # If you have already installed the previous version of Osdag in your system then skip Step/Command 1) and just run the new 2-install-osdag.sh. - - In bash, navigate to the extracted installation folder containing the shell - scripts (the folder that contains this README file) and a folder named Osdag, - and enter Command 1 , Command 2 and Command 3 given below. - - Note: After entering Command 1, while installing Miniconda3, you will be asked - whether you wish to set the system default python to Miniconda3. You need to agree - to this.After that you have to run command 2 in order for the 3rd command to work. - After that execute the 3rd steps. After 3rd step completed run the command 4.Please be sure - to have internet connection as it's required to download some files. - Step/Command 1: - bash 1-install-Miniconda3-latest-Linux-x86_64.sh - Step/Command 2: - bash 2-init-conda_base.sh - Step/Command 3: - bash 3-install-osdag.sh - Step/Command 4: - bash 4-install-texlive.sh - - - Running Osdag: - ============= - After the installation is complete, you may copy/move the extracted Osdag folder to a location of your choice (say, directly under your home folder). - You can run Osdag in two ways - 1) Using the Osdag Launcher: - To run Osdag, navigate to the Osdag folder, double click on the file named Osdag (without any extension). - This file is different from Osdag_icon.ico (although both will show the Osdag logo in the grid icon view mode). - If you are using the Unity desktop, you may also pin this launcher to the launcher sidebar. - - 2) Using the Command: - In the bash prompt, navigate to the Osdag directory and enter the following command python osdagMainPage.py - - Note that, Step/Command 2 will work only if the system default python is the one installed through Miniconda2. - Alternatively, you may specify the (installed) python you wish to use, in Command 2. - -## Contributing -Osdag invites enthusiasts with similar interest(s) to contribute to Osdag development. Your contributions can go a long way in improving the software. -Please take a moment to review the guidelines for contributing. - - * Bug reports - * Feature requests - * Pull requests - -## Bugs and known issues -Have a bug or a feature request? Please first read the issue guidelines and search for existing and closed issues. If your problem or idea has not been addressed yet, please open a new issue or post a query on the Osdags discussion forum. - -## Versioning -The latest version of Osdag can perform design for two scenarios; - -Scenario 1: Users can obtain the optimum design for a given scenario, from a suite of available options in terms of steel sections (e.g., different channel sizes and plate thicknesses) and connectors (e.g., bolts of different grades and diameters). The optimum design is selected based on the total volume of material and this design solution is detailed in the output dock and design report. - -Scenario 2: Perform a design check with a specific set of single inputs/selections in the 'Customized' option. In this case, Osdag will inform if the design checks are satisfied and suggest changes otherwise. - -The Design Report has been reformatted using the LaTeX software system through the PyLaTeX package. The report is much more detailed and shows step-by-step calculation(s) for a better user experience. - -The Shear and Moment connections available with the previous versions have been modified in terms of structure at the backend, GUI and calculations. Any know bug(s) have been fixed. - -The latest version of Osdag contains the following modules (in addition to the ones available with the previous versions): - - Beam-Beam Splice Connection - - Beam-Beam Cover Plate Bolted - Beam-Beam End Plate - Beam-Beam Cover Plate Welded - - Beam-Column Connection - - Beam-Column End Plate - - Column-Column Splice Connection - - Column-Column Cover Plate Bolted - Column-Column Cover Plate Welded - Column-Column End Plate - - Base Plate Connection - - Tension Member - - Tension Member Bolted - Tension Member Welded - -Previous Releases - -Version 2017.08.a.874e - - Bugs fixed - -Version 2017.06.a.e2dd - - This beta version of Osdag contains only the shear connection modules. - -=============================================== -The contributors of the latest version are: - -Osdag development team (2019 - Present) - -=============================== - -Project Investigator - Osdag - -Professor Siddhartha Ghosh - -=============================== - -Research Associates/Assistants - Technical and Development Team - -Mr. Danish Ansari - -Mr. Ajmal Babu MS - -Mr. N Dharma Teja - -Ms. Thushara Pushkaran - -Mr. Yash Lokhande - -Mr. Anand Swaroop - -Mr. Darshan Divesan - -Mr. Anjali Jatav - -Mr. Sourabh Das - -Ms. Deepthi Reddy - -=============================== - -Project Interns - -Mr. Ansari Mohammad Umair - -Mr. Amir Chappalwala - -Mr. Zunzunia Arsil - -Mr. Mohammad Azhar U Din Mir - -Mr. Satyam Singh Niranjan - -Mr. Anshul Kumar Singh - -Mr. Mosam Patel - -Mr. Shahadad PP - -Ms. Priti Kumari - -=============================== - -Project Management - -Ms. Usha Viswanathan - -Ms. Vineeta Parmar - -Mr. Sunil Shetye - -=============================== - -Web, Graphics, Promotions and System Administrators Team - -Ms. Sashi Rekha B M K - -Mr. Lee Thomas Stephen - -Mr. Rohan Mhatre - -Mr. Khushal Singh Rajput - -Mr. Yash Vohra - -=============================== - -Office Staff - -Ms.Komal Solanki - -Mr.Vishal Birare - -Mr. Sushant Bammkanti - -=============================== - -Acknowledgements: - -Ministry of Education (MoE), Govt. of India - -FOSSEE - -Professor Kannan Moudgalya - -Professor Prabhu Ramachandran - -Mr. Sunil Shetye - -## Copyright and license -(c) Copyright Osdag contributors 2020.
-This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the License.txt file for details regarding the license. -The beta version of Osdag is released under the terms and conditions of the GNU LESSER GENERAL PUBLIC LICENSE (LGPL) Version 3. - -=============================== End of File =============================== diff --git a/Report_functions_a.py b/Report_functions_a.py deleted file mode 100644 index fed2e89cd..000000000 --- a/Report_functions_a.py +++ /dev/null @@ -1,2553 +0,0 @@ -from builtins import str -import time -import math -from Common import * -from utils.common.is800_2007 import * -import os -# from utils.common import component -from pylatex import Document, Section, Subsection -from pylatex.utils import italic, bold -#import pdflatex -import sys -import datetime -#from PyQt5.QtCore import pyqtSlot,pyqtSignal, QObject - - -from pylatex import Document, Section, Subsection, Tabular, Tabularx,MultiColumn -from pylatex import Math, TikZ, Axis, Plot, Figure, Matrix, Alignat -from pylatex.utils import italic, NoEscape -#from pdflatex import PDFLaTeX -import os -from pylatex import Document, PageStyle, Head, MiniPage, Foot, LargeText, \ - MediumText, LineBreak, simple_page_number -from pylatex.utils import bold - - -def min_pitch(d):#TODO:Done - """ - Calculate the min pitch distance - Args: - d:Diameter of provided bolt in mm (float) - Returns: - Minimum pitch distance in mm (float) - Note: - Reference: - IS 800:2007, cl. 10.2.2 - - """ - min_pitch = 2.5*d - d = str(d) - min_pitch = str(min_pitch) - - min_pitch_eqn = Math(inline=True) - min_pitch_eqn.append(NoEscape(r'\begin{aligned}p/g_{min}&= 2.5 ~ d\\')) - min_pitch_eqn.append(NoEscape(r'&=2.5*' + d + r'\\&=' + min_pitch + r'\\')) - min_pitch_eqn.append(NoEscape(r'[Ref.~I&S~800:2007,~Cl.~10.2.2] \end{aligned}')) - - return min_pitch_eqn - - - -def cl_10_2_2_min_spacing(d, parameter='pitch'): - """ minimum spacing between two adjacent fasteners as per cl.10.2.2, IS 800:2007 - Args: - d: diameter of the fastener (int) - - Returns: - equation for the minimum spacing between two adjacent fasteners which is 2.5 * diameter of the fastener - """ - d = str(d) - min_spacing = 2.5 * d - min_spacing = str(min_spacing) - - min_spacing_eqn = Math(inline=True) - if parameter == 'pitch': - min_spacing_eqn.append(NoEscape(r'\begin{aligned}Pitch~Distance~_{min} = 2.5 ~ d\\')) - else: - min_spacing_eqn.append(NoEscape(r'\begin{aligned}Gauge~Distance~_{min} = 2.5 ~ d\\')) - min_spacing_eqn.append(NoEscape(r'= &2.5*' + d + r'&=' + min_spacing + r'\end{aligned}')) - - return min_spacing_eqn - - -def cl_10_2_3_1_max_spacing(t, parameter='pitch'): - """ maximum spacing between two adjacent fasteners as per cl.10.2.3.1, IS 800:2007 - Args: - t: thickness of the thinner plate (int) - - Returns: - equation for the maximum spacing between two adjacent fasteners which is minimum of (32*t, 300mm) - """ - t = str(t) - max_spacing = min(32 * t, 300) - max_spacing = str(max_spacing) - - max_spacing_eqn = Math(inline=True) - if parameter == 'pitch': - max_spacing_eqn.append(NoEscape(r'\begin{aligned}Pitch~Distance~_{max} = min~(32 ~ t, ~300~mm)\\')) - else: - max_spacing_eqn.append(NoEscape(r'\begin{aligned}Gauge~Distance~_{max} = min~(32 ~ t, ~300~mm)\\')) - max_spacing_eqn.append(NoEscape(r'= min (&32*' + t + r', 300~mm)&=' + max_spacing + r'\end{aligned}')) - - return max_spacing_eqn - - -def cl_10_2_4_2_min_edge_end_dist(d, bolt_hole_type='Standard', edge_type='a - Sheared or hand flame cut', parameter='end_dist'): - """Calculate minimum end and edge distance - Args: - d - Nominal diameter of fastener in mm (float) - bolt_hole_type - Either 'Standard', 'Over-sized', 'Short Slot' or 'Long Slot' (str) - edge_type - Either 'hand_flame_cut' or 'machine_flame_cut' (str) - parameter - edge or end distance required to return the specific equation (str) - Returns: - Equation for minimum end and edge distance from the centre of any hole to the nearest edge of a plate in mm (float) - Note: - Reference: - IS 800:2007, cl. 10.2.4.2 - """ - d_0 = IS800_2007.cl_10_2_1_bolt_hole_size(d, bolt_hole_type) - if edge_type == 'a - Sheared or hand flame cut': - end_edge_multiplier = 1.7 - else: - # TODO : bolt_hole_type == 'machine_flame_cut' is given in else - end_edge_multiplier = 1.5 - - min_end_edge_dist = end_edge_multiplier * d_0 - - d_0 = str(d_0) - end_edge_multiplier = str(end_edge_multiplier) - min_end_edge_dist = str(min_end_edge_dist) - - end_edge_eqn = Math(inline=True) - if parameter == 'end_dist': - end_edge_eqn.append(NoEscape(r'\begin{aligned}End~Distance~_{min} = ' + end_edge_multiplier + '~d_0\\')) - else: # parameter == 'edge_dist' - end_edge_eqn.append(NoEscape(r'\begin{aligned}Edge~Distance~_{min} = ' + end_edge_multiplier + '~d_0\\')) - - end_edge_eqn.append(NoEscape(r'\begin = ' + end_edge_multiplier + '~ ' + d_0 + '\\')) - end_edge_eqn.append(NoEscape(r'\begin = ' + min_end_edge_dist + '')) - - return end_edge_eqn - - -def cl_10_2_4_3_max_edge_dist(plate_thicknesses, f_y, corrosive_influences=False, parameter='end_dist'): - """Calculate maximum end and edge distance - Args: - plate_thicknesses - List of thicknesses in mm of outer plates (list or tuple) - f_y - Yield strength of plate material in MPa (float) - corrosive_influences - Whether the members are exposed to corrosive influences or not (Boolean) - Returns: - Maximum end and edge distance to the nearest line of fasteners from an edge of any un-stiffened part in mm (float) - Note: - Reference: - IS 800:2007, cl. 10.2.4.3 - """ - t = min(plate_thicknesses) - epsilon = math.sqrt(250 / f_y) - - if corrosive_influences is True: - max_end_edge_dist = 40.0 + 4 * t - else: - max_end_edge_dist = 12 * t * epsilon - - t = str(t) - epsilon = str(epsilon) - max_end_edge_dist = str(max_end_edge_dist) - - end_edge_eqn = Math(inline=True) - if corrosive_influences is False and parameter == 'end_dist': - end_edge_eqn.append(NoEscape(r'\begin{aligned}End~Distance~_{max} = 12~t~\epsilon~-when~members~are~not~exposed~to~corrosive~influences\\')) - end_edge_eqn.append(NoEscape(r'\begin = 12' + t + '~' + epsilon + '\\')) - else: # corrosive_influences is True and parameter is 'end_dist' - end_edge_eqn.append(NoEscape(r'\begin{aligned}End~Distance~_{max} = 40~mm~+~4~t~-when~members~are~exposed~to~corrosive~influences\\')) - end_edge_eqn.append(NoEscape(r'\begin = 40~mm~+~4~' + t + '\\')) - - if corrosive_influences is False and parameter == 'edge_dist': - end_edge_eqn.append(NoEscape(r'\begin{aligned}Edge~Distance~_{max} = 12~t~\epsilon~-when~members~are~not~exposed~to~corrosive~influences\\')) - end_edge_eqn.append(NoEscape(r'\begin = 12' + t + '~' + epsilon + '\\')) - else: # corrosive_influences is True and parameter is 'edge_dist' - end_edge_eqn.append(NoEscape(r'\begin{aligned}Edge~Distance~_{max} = 40~mm~+~4~t~-when~members~are~exposed~to~corrosive~influences\\')) - end_edge_eqn.append(NoEscape(r'\begin = 40~mm~+~4~' + t + '\\')) - - end_edge_eqn.append(NoEscape(r'\begin = ' + max_end_edge_dist + '')) - - return end_edge_eqn - -def max_pitch(t):#todo:done - """ - Calculate the maximum pitch distance - Args: - t: Thickness of thinner plate in mm (float) - Returns: - Max pitch in mm (float) - Note: - Reference: - IS 800:2007, cl. 10.2.3 - """ - t1 = str(t[0]) - t2 = str(t[0]) - max_pitch_1 = 32*min(t) - max_pitch_2 = 300 - max_pitch = min(max_pitch_1,max_pitch_2) - t = str(min(t)) - max_pitch = str(max_pitch) - - - max_pitch_eqn = Math(inline=True) - max_pitch_eqn.append(NoEscape(r'\begin{aligned}p/g_{max}&=\min(32~t,~300~mm)\\')) - max_pitch_eqn.append(NoEscape(r'&=\min(32 *~' + t+ r',~ 300 ~mm)\\&='+max_pitch+r'\\')) - max_pitch_eqn.append(NoEscape(r'&t = min('+t1+','+t2+r'\\')) - max_pitch_eqn.append(NoEscape(r'[Ref.~IS~&800:2007,~Cl.~10.2.3]\end{aligned}')) - - return max_pitch_eqn - - -def min_edge_end(d_0,edge_type): - if edge_type == 'a - Sheared or hand flame cut': - factor = 1.7 - else: - factor = 1.5 - min_edge_dist = round(factor * d_0,2) - - min_edge_dist = str(min_edge_dist) - - factor = str(factor) - d_0 = str(d_0) - - min_end_edge_eqn = Math(inline=True) - min_end_edge_eqn.append(NoEscape(r'\begin{aligned}e/e`_{min} &=[1.5~or~ 1.7] * d_0\\')) - min_end_edge_eqn.append(NoEscape(r'&='+factor + r'*' + d_0+r'='+min_edge_dist+r' \end{aligned}')) - return min_end_edge_eqn - - -#TODO: consider using max_edge_end_new instead of this function in all modules -def max_edge_end(f_y,t): - - epsilon = round(math.sqrt(250/f_y),2) - max_edge_dist = round(12*t*epsilon,2) - max_edge_dist = str(max_edge_dist) - t = str(t) - f_y = str(f_y) - - max_end_edge_eqn = Math(inline=True) - max_end_edge_eqn.append(NoEscape(r'\begin{aligned}e/e`_{max} &= 12~ t~ \varepsilon&\\')) - max_end_edge_eqn.append(NoEscape(r'\varepsilon &= \sqrt{\frac{250}{f_y}}\\')) - max_end_edge_eqn.append(NoEscape(r'e/e`_{max}&=12 ~*'+ t + r'*\sqrt{\frac{250}{'+f_y+r'}}\\ &='+max_edge_dist+r'\\ \end{aligned}')) - return max_end_edge_eqn - - -def max_edge_end_new(t_fu_fy,corrosive_influences): - t_epsilon_considered = t_fu_fy[0][0] * math.sqrt(250 / float(t_fu_fy[0][2])) - t_considered = t_fu_fy[0][0] - t_min = t_considered - for i in t_fu_fy: - t = i[0] - f_y = i[2] - epsilon = math.sqrt(250 / f_y) - if t * epsilon <= t_epsilon_considered: - t_epsilon_considered = t * epsilon - t_considered = t - if t < t_min: - t_min = t - - if corrosive_influences is True: - max_edge_dist = 40.0 + 4 * t_min - else: - max_edge_dist = 12 * t_epsilon_considered - - max_edge_dist = str(max_edge_dist) - t1=str(t_fu_fy[0][0]) - t2=str(t_fu_fy[1][0]) - fy1 = str(t_fu_fy[0][2]) - fy2 = str(t_fu_fy[1][2]) - max_end_edge_eqn = Math(inline=True) - if corrosive_influences is False: - max_end_edge_eqn.append(NoEscape(r'\begin{aligned}e/e`_{max} &= 12~ t~ \varepsilon&\\')) - max_end_edge_eqn.append(NoEscape(r'\varepsilon &= \sqrt{\frac{250}{f_y}}\\')) - max_end_edge_eqn.append(NoEscape(r'e1 &= 12 ~*' + t1 + r'*\sqrt{\frac{250}{' + fy1 + r'}}\\')) - max_end_edge_eqn.append(NoEscape(r'e2 &= 12 ~*' + t2 + r'*\sqrt{\frac{250}{' + fy2 + r'}}\\')) - max_end_edge_eqn.append(NoEscape(r'e/e`_{max}&=min(e1,e2)\\')) - max_end_edge_eqn.append(NoEscape(r' &=' + max_edge_dist + r'\\ \end{aligned}')) - else: - max_end_edge_eqn.append(NoEscape(r'e/e`_{max}&=40 + 4*t \\')) - max_end_edge_eqn.append(NoEscape(r'Where, t&= min(' + t1 +', '+t2+r')\\')) - max_end_edge_eqn.append(NoEscape(r'e/e`_{max}&='+max_edge_dist+r' \end{aligned}')) - return max_end_edge_eqn - - -def bolt_shear_prov(f_ub,n_n,a_nb,gamma_mb,bolt_shear_capacity): - f_ub = str(f_ub) - n_n = str(n_n) - a_nb = str(a_nb) - gamma_mb= str(gamma_mb) - bolt_shear_capacity=str(bolt_shear_capacity) - bolt_shear_eqn = Math(inline=True) - bolt_shear_eqn.append(NoEscape(r'\begin{aligned}V_{dsb} &= \frac{f_{ub} ~n_n~ A_{nb}}{\sqrt{3} ~\gamma_{mb}}\\')) - bolt_shear_eqn.append(NoEscape(r'&= \frac{'+f_ub+'*'+n_n+'*'+a_nb+'}{\sqrt{3}~*~'+ gamma_mb+r'}\\')) - bolt_shear_eqn.append(NoEscape(r'&= '+bolt_shear_capacity+r'\end{aligned}')) - return bolt_shear_eqn - - -def bolt_bearing_prov(k_b,d,conn_plates_t_fu_fy,gamma_mb,bolt_bearing_capacity): - t_fu_prev = conn_plates_t_fu_fy[0][0] * conn_plates_t_fu_fy[0][1] - t = conn_plates_t_fu_fy[0][0] - f_u = conn_plates_t_fu_fy[0][1] - for i in conn_plates_t_fu_fy: - t_fu = i[0] * i[1] - if t_fu <= t_fu_prev: - t = i[0] - f_u = i[1] - k_b = str(k_b) - d = str(d) - t = str(t) - f_u= str(f_u) - gamma_mb=str(gamma_mb) - bolt_bearing_capacity = str(bolt_bearing_capacity) - bolt_bearing_eqn = Math(inline=True) - bolt_bearing_eqn.append(NoEscape(r'\begin{aligned}V_{dpb} &= \frac{2.5~ k_b~ d~ t~ f_u}{\gamma_{mb}}\\')) - bolt_bearing_eqn.append(NoEscape(r'&= \frac{2.5~*'+ k_b+'*'+ d+'*'+t+'*'+f_u+'}{'+gamma_mb+r'}\\')) - bolt_bearing_eqn.append(NoEscape(r'&='+bolt_bearing_capacity+r'\end{aligned}')) - - return bolt_bearing_eqn - - -def bolt_capacity_prov(bolt_shear_capacity,bolt_bearing_capacity,bolt_capacity): - bolt_shear_capacity = str(bolt_shear_capacity) - bolt_bearing_capacity = str(bolt_bearing_capacity) - bolt_capacity = str(bolt_capacity) - bolt_capacity_eqn = Math(inline=True) - bolt_capacity_eqn.append(NoEscape(r'\begin{aligned}V_{db} &= min~ (V_{dsb}, V_{dpb})\\')) - bolt_capacity_eqn.append(NoEscape(r'&= min~ ('+bolt_shear_capacity+','+ bolt_bearing_capacity+r')\\')) - bolt_capacity_eqn.append(NoEscape(r'&='+ bolt_capacity+r'\end{aligned}')) - - return bolt_capacity_eqn - - -def cl_10_3_5_bearing_bolt_tension_resistance(f_ub, f_yb, A_sb, A_n, safety_factor_parameter=KEY_DP_FAB_FIELD): - """ - Calculate design tensile strength of bearing bolt - Args: - f_ub - Ultimate tensile strength of the bolt in MPa (float) - f_yb - Yield strength of the bolt in MPa (float) - A_sb - Shank area of bolt in sq. mm (float) - A_n - Net tensile stress area of the bolts as per IS 1367 in sq. mm (float) - return: - T_db - Design tensile strength of bearing bolt in N (float) - Note: - Reference: - IS 800:2007, cl 10.3.5 - """ - f_ub = str(f_ub) - f_yb = str(f_yb) - A_sb = str(A_sb) - A_n = str(A_n) - gamma_mb = IS800_2007.cl_5_4_1_Table_5['gamma_mb'][safety_factor_parameter] - gamma_m0 = IS800_2007.cl_5_4_1_Table_5['gamma_m0']['yielding'] - tension_resistance = Math(inline=True) - tension_resistance.append(NoEscape(r'\begin{aligned} T_{db} = 0.90~f_{ub}~A_n < f_{yb}~A_{sb}~(\gamma_{mb}~/~\gamma_{m0}) \\')) - tension_resistance.append(NoEscape(r'\begin = 0.90~' + f_ub + '~ ' + A_n + '< ' + f_yb + '~ ' + A_sb + '~(' + gamma_mb + '~/~' + gamma_m0 + '')) - tension_resistance.append(NoEscape(r'\begin = 0.90~' + f_ub + '~ ' + A_n + '')) - - return tension_resistance - - -def cl_10_3_6_bearing_bolt_combined_shear_and_tension(V_sb, V_db, T_b, T_db, value): - """Check for bolt subjected to combined shear and tension - Args: - V_sb - factored shear force acting on the bolt, - V_db - design shear capacity, - T_b - factored tensile force acting on the bolt, - T_db - design tension capacity. - return: combined shear and friction value - Note: - Reference: - IS 800:2007, cl 10.3.6 - """ - V_sb = str(V_sb) - V_db = str(V_db) - T_b = str(T_b) - T_db = str(T_db) - value = str(value) - - combined_capacity_eqn = Math(inline=True) - combined_capacity_eqn.append(NoEscape(r'\begin{aligned}\bigg(\frac{V_{sb}}{V_{db}}\bigg)^2 + \bigg(\frac{T_{b}}{T_{db}}\bigg)^2 \leq 1.0\\')) - combined_capacity_eqn.append(NoEscape(r'\bigg(\frac{' + V_sb + '}{' + V_db + '}\bigg)^2 + \bigg(\frac{' + T_b + '}{' + T_db + '}\bigg)^2 = ' - + value + '')) - - return combined_capacity_eqn - - -def HSFG_bolt_capacity_prov(mu_f,n_e,K_h,fub,Anb,gamma_mf,capacity): - mu_f = str(mu_f) - n_e = str(n_e) - K_h = str(K_h) - fub = str(fub) - Anb = str(Anb) - gamma_mf = str(gamma_mf) - capacity = str(capacity) - - HSFG_bolt_capacity_eqn = Math(inline=True) - HSFG_bolt_capacity_eqn.append(NoEscape(r'\begin{aligned}V_{dsf} & = \frac{\mu_f~ n_e~ K_h~ F_o}{\gamma_{mf}}\\')) - HSFG_bolt_capacity_eqn.append(NoEscape(r'& Where, F_o = 0.7 * f_{ub} A_{nb}\\')) - HSFG_bolt_capacity_eqn.append(NoEscape(r'V_{dsf} & = \frac{'+ mu_f + '*' + n_e + '*' + K_h +'* 0.7 *' +fub+'*'+Anb +r'}{'+gamma_mf+r'}\\')) - HSFG_bolt_capacity_eqn.append(NoEscape(r'& ='+capacity+r'\end{aligned}')) - - return HSFG_bolt_capacity_eqn - - -def get_trial_bolts(V_u, A_u,bolt_capacity,multiple=1,conn=None): - - res_force = math.sqrt(V_u**2+ A_u**2) - trial_bolts = multiple * math.ceil(res_force/bolt_capacity) - V_u=str(V_u) - A_u=str(A_u) - bolt_capacity=str(bolt_capacity) - trial_bolts=str(trial_bolts) - trial_bolts_eqn = Math(inline=True) - trial_bolts_eqn.append(NoEscape(r'\begin{aligned}R_{u} &= \sqrt{V_u^2+A_u^2}\\')) - trial_bolts_eqn.append(NoEscape(r'n_{trial} &= R_u/ V_{bolt}\\')) - if conn == "flange_web": - trial_bolts_eqn.append(NoEscape(r'R_{u} &= \frac{2*\sqrt{' + V_u + r'^2+' + A_u + r'^2}}{' + bolt_capacity + r'}\\')) - else: - trial_bolts_eqn.append(NoEscape(r'R_{u} &= \frac{\sqrt{'+V_u+r'^2+'+A_u+r'^2}}{'+bolt_capacity+ r'}\\')) - trial_bolts_eqn.append(NoEscape(r'&='+trial_bolts+ r'\end{aligned}')) - return trial_bolts_eqn - - -def parameter_req_bolt_force(bolts_one_line,gauge,ymax,xmax,bolt_line,pitch,length_avail, conn=None): - """ - bolts_one_line =n_r - bolt_line = n_c - - for column splice - bolts_one_line =n_c - bolt_line = n_r - """ - bolts_one_line = str(bolts_one_line) - ymax = str(ymax) - xmax = str(xmax) - gauge = str(gauge) - pitch = str(pitch) - bolt_line = str(bolt_line) - length_avail = str(length_avail) - - parameter_req_bolt_force_eqn = Math(inline=True) - parameter_req_bolt_force_eqn.append(NoEscape(r'\begin{aligned} l_n~~~ &= length~available \\')) - if conn == 'fin': - parameter_req_bolt_force_eqn.append(NoEscape(r' l_n~~~ &= p * (n_r - 1)\\')) - elif conn == 'beam_beam': - parameter_req_bolt_force_eqn.append(NoEscape(r' l_n~~~ &= g * (n_r - 1)\\')) - elif conn== 'col_col': - parameter_req_bolt_force_eqn.append(NoEscape(r' l_n~~~ &= g * (n_c - 1)\\')) - parameter_req_bolt_force_eqn.append(NoEscape(r' &= '+gauge+r' * (' + bolts_one_line + r' - 1)\\')) - parameter_req_bolt_force_eqn.append(NoEscape(r' & ='+length_avail+ r'\\')) - - parameter_req_bolt_force_eqn.append(NoEscape(r' y_{max} &= l_n / 2\\')) - parameter_req_bolt_force_eqn.append(NoEscape(r' &= '+length_avail+ r' / 2 \\')) - parameter_req_bolt_force_eqn.append(NoEscape(r' & =' + ymax + r'\\')) - - - if conn == 'fin': - parameter_req_bolt_force_eqn.append(NoEscape(r'x_{max} &= g * (n_c - 1)/2 \\')) - parameter_req_bolt_force_eqn.append(NoEscape(r' &= '+pitch+r' * (\frac{'+bolt_line+ r'}{2} - 1) / 2 \\')) - elif conn == 'col_col': - parameter_req_bolt_force_eqn.append(NoEscape(r'x_{max} &= p * (\frac{n_r}{2} - 1) / 2 \\')) - parameter_req_bolt_force_eqn.append(NoEscape(r' &= ' + pitch + r' * (\frac{' + bolt_line + r'}{2} - 1) / 2 \\')) - else: - parameter_req_bolt_force_eqn.append(NoEscape(r'x_{max} &= p * (\frac{n_c}{2} - 1) / 2 \\')) - parameter_req_bolt_force_eqn.append(NoEscape(r' &= ' + pitch + r' * (\frac{' + bolt_line + r'}{2} - 1) / 2 \\')) - - parameter_req_bolt_force_eqn.append(NoEscape(r' & =' + xmax + r'\end{aligned}')) - - return parameter_req_bolt_force_eqn - - -def moment_demand_req_bolt_force(shear_load, web_moment,moment_demand,ecc): - - ecc = str(ecc) - web_moment = str(web_moment) - moment_demand = str(moment_demand) - shear_load = str(shear_load) - loads_req_bolt_force_eqn = Math(inline=True) - - loads_req_bolt_force_eqn.append(NoEscape(r'\begin{aligned} M_d &= (V_u * ecc + M_w)\\')) - loads_req_bolt_force_eqn.append(NoEscape(r' &= \frac{('+shear_load+' * 10^3 *'+ecc+' + '+web_moment+r'*10^6)}{10^6}\\')) - loads_req_bolt_force_eqn.append(NoEscape(r' & =' + moment_demand + r'\end{aligned}')) - return loads_req_bolt_force_eqn - -def design_capacity_of_end_plate(M_dp,b_eff,f_y,gamma_m0,t_p): - M_dp= str(M_dp) - t_p = str(t_p) - b_eff= str(b_eff) - f_y= str(f_y) - gamma_m0= str(gamma_m0) - - design_capacity_of_end_plate= Math(inline=True) - - design_capacity_of_end_plate.append(NoEscape(r'\begin{aligned} M_{dp} & = { \frac{ b_{eff} *t_p^2 *f_y}{ 4*\gamma_{m0}}}\\')) - - design_capacity_of_end_plate.append(NoEscape(r'&={\frac{' + b_eff +r'*'+t_p+r'^2'+' *'+f_y + r'}{4*'+gamma_m0 + r'}}\\')) - design_capacity_of_end_plate.append(NoEscape(r'&=' +M_dp + r'\end{aligned}')) - return design_capacity_of_end_plate - -def Vres_bolts(bolts_one_line,ymax,xmax,bolt_line,axial_load - ,moment_demand,r,vbv,tmv,tmh,abh,vres,shear_load,conn=None): #vres bolt web - """ - bolts_one_line =n_r - bolt_line = n_c - - for column_column splice connection - bolts_one_line =n_c - bolt_line = n_r - """ - bolts_one_line =str(bolts_one_line) - ymax =str(ymax) - xmax =str(xmax) - bolt_line = str(bolt_line) - - r = str(r) - moment_demand = str(moment_demand) - axial_load =str(axial_load) - shear_load = str(shear_load) - vbv =str(vbv) - tmv =str(tmv) - tmh =str(tmh) - abh =str(abh) - vres = str(vres) - Vres_bolts_eqn = Math(inline=True) - if conn == "beam_beam": - Vres_bolts_eqn.append(NoEscape(r'\begin{aligned} vbv~~ &= V_u / (n_r * (n_c/2))\\')) - Vres_bolts_eqn.append(NoEscape(r' &= \frac{'+shear_load+ '}{ ('+bolts_one_line +'*('+ bolt_line+r'/2))}\\')) - elif conn == "col_col": - Vres_bolts_eqn.append(NoEscape(r'\begin{aligned} vbv~~ &= V_u / ((n_r/2) * n_c)\\')) - Vres_bolts_eqn.append(NoEscape(r' &= \frac{' + shear_load + '}{ (' + bolts_one_line + '*(' + bolt_line + r'/2))}\\')) - else: - Vres_bolts_eqn.append(NoEscape(r'\begin{aligned} vbv~~ &= V_u / (n_r * n_c)\\')) - Vres_bolts_eqn.append(NoEscape(r' &= \frac{' + shear_load + '}{ (' + bolts_one_line + '*' + bolt_line + r')}\\')) - - Vres_bolts_eqn.append(NoEscape(r' & =' + vbv + r'\\')) - Vres_bolts_eqn.append(NoEscape(r'tmh~ &= \frac{M_d * y_{max} }{ \Sigma r_i^2} \\')) - Vres_bolts_eqn.append(NoEscape(r' &= \frac{'+moment_demand+' *'+ ymax+'}{'+r+r'}\\')) - Vres_bolts_eqn.append(NoEscape(r' & =' + tmh + r'\\')) - - Vres_bolts_eqn.append(NoEscape(r' tmv ~&= \frac{M_d * x_{max}}{\Sigma r_i^2}\\')) - Vres_bolts_eqn.append(NoEscape(r'&= \frac{' +moment_demand+' * '+xmax+'}{'+r+ r'}\\')) - Vres_bolts_eqn.append(NoEscape(r' & =' + tmv + r'\\')) - if conn == "beam_beam": - Vres_bolts_eqn.append(NoEscape(r' abh~ & = \frac{A_u }{(n_r * n_c/2)}\\')) - Vres_bolts_eqn.append(NoEscape(r' & =\frac{' + axial_load + '}{ (' + bolts_one_line + ' *(' + bolt_line + r'/2))}\\')) - elif conn == "col_col": - Vres_bolts_eqn.append(NoEscape(r' abh~ & = \frac{A_u }{((n_r/2) * n_c)}\\')) - Vres_bolts_eqn.append(NoEscape(r' & =\frac{' + axial_load + '}{ (' + bolts_one_line + ' *(' + bolt_line + r'/2))}\\')) - else: - Vres_bolts_eqn.append(NoEscape(r' abh~ & = \frac{A_u }{(n_r * n_c)}\\')) - Vres_bolts_eqn.append(NoEscape(r' & =\frac{' + axial_load + '}{ (' + bolts_one_line + ' *' + bolt_line + r')}\\')) - - Vres_bolts_eqn.append(NoEscape(r' & =' + abh + r'\\')) - Vres_bolts_eqn.append(NoEscape(r' vres &=\sqrt{(vbv +tmv) ^ 2 + (tmh+abh) ^ 2}\\')) - # Vres_bolts_eqn.append(NoEscape(r' vres &= \sqrt((vbv + tmv) ^ 2 + (tmh + abh) ^ 2)\\')) - Vres_bolts_eqn.append(NoEscape(r' &= \sqrt{('+vbv+' +'+ tmv+') ^2 + ('+tmh +'+'+ abh+r') ^ 2}\\')) - Vres_bolts_eqn.append(NoEscape(r' & =' + vres + r'\end{aligned}')) - - return Vres_bolts_eqn - - -def forces_in_web(Au,T,A,t,D,Zw,Mu,Z,Mw,Aw): - Au = str(Au) - T = str(T) - A = str(A) - t = str(t) - D = str(D) - Zw = str(Zw) - Mu = str(Mu) - Z = str(Z) - Mw = str(Mw) - Aw = str(Aw) - forcesinweb_eqn = Math(inline=True) - - forcesinweb_eqn.append(NoEscape(r'\begin{aligned}A_w &= Axial~ force~ in~ web \\')) - forcesinweb_eqn.append(NoEscape(r' &= \frac{(D- 2*T)*t* Au }{A} \\')) - forcesinweb_eqn.append(NoEscape(r'&= \frac{(' + D + '- 2*' + T + ')*' + t + '*' + Au + ' }{' + A + r'} \\')) - forcesinweb_eqn.append(NoEscape(r'&=' + Aw + r'~ kN\\')) - forcesinweb_eqn.append(NoEscape( r'M_w &= Moment ~in ~web \\')) - forcesinweb_eqn.append(NoEscape(r' &= \frac{Z_w * Mu}{Z} \\')) - forcesinweb_eqn.append(NoEscape(r'&= \frac{' + Zw + ' * ' + Mu + '}{' + Z + r'} \\')) - forcesinweb_eqn.append(NoEscape(r'&=' + Mw + r'~{kNm}\end{aligned}')) - return forcesinweb_eqn - - -def forces_in_flange(Au, B,T,A,D,Mu,Mw,Mf,Af,ff): - Au =str(Au) - B=str(B) - T=str(T) - A=str(A) - D=str(D) - Mu=str(Mu) - Mw=str(Mw) - Mf=str(Mf) - Af=str(Af) - ff = str(ff) - forcesinflange_eqn= Math(inline=True) - forcesinflange_eqn.append(NoEscape(r'\begin{aligned} A_f&= Axial~force~ in ~flange \\')) - forcesinflange_eqn.append(NoEscape(r'&= \frac{Au * B *T}{A} \\')) - forcesinflange_eqn.append(NoEscape(r'&= \frac{' + Au + ' * ' + B + '*' + T + '}{' + A + r'} \\')) - forcesinflange_eqn.append(NoEscape(r'&=' + Af + r'~ kN\\')) - forcesinflange_eqn.append(NoEscape(r'M_f& =Moment~ in~ flange \\')) - forcesinflange_eqn.append(NoEscape(r' & = Mu-M_w\\')) - forcesinflange_eqn.append(NoEscape(r'&= ' + Mu + '-' + Mw + r'\\')) - forcesinflange_eqn.append(NoEscape(r'&=' + Mf + r'~{kNm}\\')) - forcesinflange_eqn.append(NoEscape(r' F_f& =flange~force \\')) - forcesinflange_eqn.append(NoEscape(r'& = \frac{M_f *10^3}{D-T} + A_f \\')) - forcesinflange_eqn.append(NoEscape(r'&= \frac{' + Mf + '* 10^3}{' + D + '-' + T + '} +' + Af + r' \\')) - forcesinflange_eqn.append(NoEscape(r'&=' + ff + r'~kN \end{aligned}')) - return forcesinflange_eqn - - -def min_plate_ht_req(beam_depth,min_plate_ht): - beam_depth = str(beam_depth) - min_plate_ht = str(round(min_plate_ht,2)) - min_plate_ht_eqn = Math(inline=True) - min_plate_ht_eqn.append(NoEscape(r'\begin{aligned}0.6 * d_b&= 0.6 * '+ beam_depth + r'='+min_plate_ht+r'\end{aligned}')) - return min_plate_ht_eqn - - -def min_flange_plate_ht_req(beam_width,min_flange_plate_ht):## when only outside plate is considered - beam_width = str(beam_width) - min_flange_plate_ht = str(min_flange_plate_ht) - min_flange_plate_ht_eqn = Math(inline=True) - min_flange_plate_ht_eqn.append(NoEscape(r'\begin{aligned}min~flange~plate~ht &= beam~width\\')) - min_flange_plate_ht_eqn.append(NoEscape(r'&='+min_flange_plate_ht+r'\end{aligned}')) - - return min_flange_plate_ht_eqn - - -def min_inner_flange_plate_ht_req(beam_width, web_thickness,root_radius,min_inner_flange_plate_ht): ## when inside and outside plate is considered #todo - beam_width = str(beam_width) ### same function used for max height - min_inner_flange_plate_ht = str(min_inner_flange_plate_ht) - web_thickness=str(web_thickness) - root_radius=str(root_radius) - min_inner_flange_plate_ht_eqn = Math(inline=True) - min_inner_flange_plate_ht_eqn.append(NoEscape(r'\begin{aligned}&= \frac{B -t- (2*R1)}{2}\\')) - min_inner_flange_plate_ht_eqn.append(NoEscape(r'&=\frac{'+beam_width+ r' -' +web_thickness+ r' - 2*'+ root_radius+r'}{2}\\')) - min_inner_flange_plate_ht_eqn.append(NoEscape(r'&='+min_inner_flange_plate_ht+r'\end{aligned}')) - - return min_inner_flange_plate_ht_eqn - - -def max_plate_ht_req(connectivity,beam_depth, beam_f_t, beam_r_r, notch, max_plate_h): - beam_depth = str(beam_depth) - beam_f_t = str(beam_f_t) - beam_r_r = str(beam_r_r) - max_plate_h = str(max_plate_h) - notch = str(notch) - max_plate_ht_eqn = Math(inline=True) - if connectivity in VALUES_CONN_1: - max_plate_ht_eqn.append(NoEscape(r'\begin{aligned} &d_b - 2 (t_{bf} + r_{b1} + gap)\\')) - max_plate_ht_eqn.append(NoEscape(r'&='+beam_depth+ '- 2* (' + beam_f_t + '+' + beam_r_r +r'+ 10)\\')) - else: - max_plate_ht_eqn.append(NoEscape(r'\begin{aligned} &d_b - t_{bf} + r_{b1} - notch_h\\')) - max_plate_ht_eqn.append(NoEscape(r'&=' + beam_depth + '-' + beam_f_t + '+' + beam_r_r + '-'+ notch+ r'\\')) - max_plate_ht_eqn.append(NoEscape(r'&=' + max_plate_h + '\end{aligned}')) - return max_plate_ht_eqn - -def disp_clause(disp,clause): - disp_clause_eqn = Math(inline=True) - - disp_clause_eqn.append(NoEscape(r'\begin{aligned}&'+ disp+r'\\')) - disp_clause_eqn.append(NoEscape(r'&'+clause+r'\end{aligned}')) - return disp_clause_eqn - -def end_plate_ht_req(D,e,h_p): - D = str(D) - h_p = str(h_p) - e = str(e) - end_plate_ht_eqn = Math(inline=True) - - end_plate_ht_eqn.append(NoEscape(r'\begin{aligned} &D + 4*e \\')) - end_plate_ht_eqn.append(NoEscape(r'&=' + D + '+' + ' 4*' + e + r'\\')) - end_plate_ht_eqn.append(NoEscape(r'&=' + h_p + '\end{aligned}')) - return end_plate_ht_eqn - -def end_plate_thk_req(M_ep,b_eff,f_y,gamma_m0,t_p): - M_ep= str(M_ep) - t_p = str(t_p) - b_eff= str(b_eff) - f_y= str(f_y) - gamma_m0= str(gamma_m0) - - end_plate_thk_eqn = Math(inline=True) - - end_plate_thk_eqn.append(NoEscape(r'\begin{aligned} t_p &= {\sqrt{\frac{ M_{ep}* 4*\gamma_{m0}}{ b_{eff}*f_y}}}\\')) - - end_plate_thk_eqn.append(NoEscape(r'&={\sqrt{\frac{' + M_ep + '*4'+'*' +gamma_m0 + r'}{'+b_eff+ r'*' + f_y + r' }}}\\')) - end_plate_thk_eqn.append(NoEscape(r'&=' + t_p + '\end{aligned}')) - return end_plate_thk_eqn - - - -def moment_acting_on_end_plate(M_ep,b_eff,f_y,gamma_m0,t_p): - M_ep= str(M_ep) - t_p = str(t_p) - b_eff= str(b_eff) - f_y= str(f_y) - - gamma_m0= str(gamma_m0) - - moment_acting_on_end_plate= Math(inline=True) - - moment_acting_on_end_plate.append(NoEscape(r'\begin{aligned} M_{ep}&= {\frac{b_{eff} *t_p^2 *f_y}{ 4*\gamma_{m0}}}\\')) - - moment_acting_on_end_plate.append(NoEscape(r'&={\frac{' + b_eff +'*'+t_p+'^2'+' *'+f_y + '}{4*'+gamma_m0 + r'}}\\')) - moment_acting_on_end_plate.append(NoEscape(r'&=' +M_ep + '\end{aligned}')) - return moment_acting_on_end_plate - - -def min_plate_length_req(min_pitch, min_end_dist,bolt_line,min_length): - min_pitch = str(min_pitch) - min_end_dist = str(min_end_dist) - bolt_line = str(bolt_line) - min_length = str(min_length) - min_plate_length_eqn = Math(inline=True) - min_plate_length_eqn.append(NoEscape(r'\begin{aligned} &2*e_{min} + (n~c-1) * p_{min})\\')) - min_plate_length_eqn.append(NoEscape(r'&=2*' + min_end_dist + '+(' + bolt_line + '-1) * ' + min_pitch + r'\\')) - min_plate_length_eqn.append(NoEscape(r'&=' + min_length + '\end{aligned}')) - return min_plate_length_eqn - - -def min_flange_plate_length_req(min_pitch, min_end_dist,bolt_line,min_length,gap,sec =None): - min_pitch = str(min_pitch) - min_end_dist = str(min_end_dist) - bolt_line = str(bolt_line) - min_length = str(min_length) - gap = str(gap) - min_flange_plate_length_eqn = Math(inline=True) - if sec =="column": - min_flange_plate_length_eqn.append(NoEscape(r'\begin{aligned} & 2[2*e_{min} + ({\frac{n_r}{2}}-1) * p_{min})]\\')) - min_flange_plate_length_eqn.append(NoEscape(r'& +\frac{gap}{2}]\\')) - min_flange_plate_length_eqn.append(NoEscape(r'&=2*[(2*' + min_end_dist +r' + (\frac{'+bolt_line+r'}{2}' + r'-1) * ' + min_pitch + r'\\')) - min_flange_plate_length_eqn.append(NoEscape(r'&= + \frac{'+gap+r'}{2}]\\')) - min_flange_plate_length_eqn.append(NoEscape(r'&=' + min_length + '\end{aligned}')) - else: - min_flange_plate_length_eqn.append(NoEscape(r'\begin{aligned} & 2[2*e_{min} + ({\frac{n_c}{2}}-1) * p_{min})]\\')) - min_flange_plate_length_eqn.append(NoEscape(r'& +\frac{gap}{2}]\\')) - min_flange_plate_length_eqn.append(NoEscape(r'&=2*[(2*' + min_end_dist + r' + (\frac{' + bolt_line + r'}{2}' + r'-1) * ' + min_pitch + r'\\')) - min_flange_plate_length_eqn.append(NoEscape(r'&= + \frac{' + gap + r'}{2}]\\')) - min_flange_plate_length_eqn.append(NoEscape(r'&=' + min_length + '\end{aligned}')) - return min_flange_plate_length_eqn - - -def min_plate_thk_req(t_w): - t_w = str(t_w) - min_plate_thk_eqn = Math(inline=True) - min_plate_thk_eqn.append(NoEscape(r'\begin{aligned} t_w='+t_w+'\end{aligned}')) - return min_plate_thk_eqn - - -def shear_yield_prov(h,t, f_y, gamma, V_dg,multiple=1): - h = str(h) - t = str(t) - f_y = str(f_y) - gamma = str(gamma) - V_dg = str(V_dg) - - multiple = str(multiple) - shear_yield_eqn = Math(inline=True) - shear_yield_eqn.append(NoEscape(r'\begin{aligned} V_{dy} &= \frac{A_v*f_y}{\sqrt{3}*\gamma_{mo}}\\')) - shear_yield_eqn.append(NoEscape(r'&=\frac{'+multiple+'*'+h+'*'+t+'*'+f_y+'}{\sqrt{3}*'+gamma+r'}\\')) - shear_yield_eqn.append(NoEscape(r'&=' + V_dg + '\end{aligned}')) - return shear_yield_eqn - - -def shear_rupture_prov(h, t, n_r, d_o, fu,v_dn,multiple =1): - h = str(h) - t = str(t) - n_r = str(n_r) - d_o = str(d_o) - f_u = str(fu) - v_dn = str(v_dn) - multiple = str(multiple) - shear_rup_eqn = Math(inline=True) - shear_rup_eqn.append(NoEscape(r'\begin{aligned} V_{dn} &= \frac{0.75*A_{vn}*f_u}{\sqrt{3}*\gamma_{mo}}\\')) - shear_rup_eqn.append(NoEscape(r'&='+multiple+ r'*('+h+'-('+n_r+'*'+d_o+'))*'+t+'*'+f_u+r'\\')) - shear_rup_eqn.append(NoEscape(r'&=' + v_dn + '\end{aligned}')) - return shear_rup_eqn - -def vres_cap_bolt_check(V_u, A_u,bolt_capacity,bolt_req,multiple=1,conn=None): - - res_force = math.sqrt(V_u**2+ A_u**2) - trial_bolts = multiple * math.ceil(res_force/bolt_req) - V_u=str(V_u) - A_u=str(A_u) - bolt_req =str(bolt_req) - bolt_capacity=str(bolt_capacity) - trial_bolts=str(trial_bolts) - trial_bolts_eqn = Math(inline=True) - trial_bolts_eqn.append(NoEscape(r'\begin{aligned}R_{u} &= 2* \sqrt{V_u^2+A_u^2}\\')) - trial_bolts_eqn.append(NoEscape(r' V_{res} &= R_u/ bolt_{req}\\')) - if conn == "flange_web": - trial_bolts_eqn.append(NoEscape(r' &= \frac{2*\sqrt{' + V_u + r'^2+' + A_u + r'^2}}{' + bolt_req + r'}\\')) - else: - trial_bolts_eqn.append(NoEscape(r' &= \frac{\sqrt{'+V_u+r'^2+'+A_u+r'^2}}{'+bolt_req+ r'}\\')) - trial_bolts_eqn.append(NoEscape(r'&='+bolt_capacity+ r'\end{aligned}')) - return trial_bolts_eqn - -def section_classification(class_of_section=None): - """ - Find class of the section - Args: - class_of_section: - Returns: - Note: - Reference: - [Ref: Table 2, cl. 3.7.2 and 3.7.4 IS 800:2007] - - """ - section_classification_eqn = Math(inline=True) - if class_of_section == int(1): - section_classification_eqn.append(NoEscape( r'\begin{aligned} &Plastic \end{aligned}')) - section_classification_eqn.append(NoEscape(r' &[Ref: Table 2, cl. 3.7.2 and 3.7.4 IS 800:2007]\end{aligned}')) - elif class_of_section == int(2): - section_classification_eqn.append(NoEscape( r'\begin{aligned} &Compact \end{aligned}')) - section_classification_eqn.append(NoEscape(r' &[Ref: Table 2, cl. 3.7.2 and 3.7.4 IS 800:2007]\end{aligned}')) - else: - section_classification_eqn.append(NoEscape( r'\begin{aligned} &Semi-Compact \end{aligned}')) - section_classification_eqn.append(NoEscape(r' &[Ref: Table 2, cl. 3.7.2 and 3.7.4 IS 800:2007]\end{aligned}')) - - return section_classification_eqn - - -# def shear_Rupture_prov_weld(h, t, fu,v_dn,gamma_mo): #weld -# h = str(h) -# t = str(t) -# gamma_mo = str(gamma_mo) -# f_u = str(fu) -# v_dn = str(v_dn) -# -# shear_rup_eqn = Math(inline=True) -# shear_rup_eqn.append(NoEscape(r'\begin{aligned} V_{dn} &= \frac{0.9*A_{vn}*f_u}{\sqrt{3}*\gamma_{mo}}\\')) -# shear_rup_eqn.append(NoEscape(r'&=(0.9*'+h+'*'+t+'*'+f_u+'}{\sqrt{3}*' +gamma_mo+ r'}\\')) -# shear_rup_eqn.append(NoEscape(r'&=' + v_dn + '\end{aligned}')) -# return shear_rup_eqn - -# def shear_rupture_prov_beam(h, t, n_r, d_o, fu,v_dn): -# h = str(h) -# t = str(t) -# n_r = str(n_r) -# d_o = str(d_o) -# f_u = str(fu) -# v_dn = str(v_dn) -# shear_rup_eqn = Math(inline=True) -# shear_rup_eqn.append(NoEscape(r'\begin{aligned} V_{dn} &= \frac{0.9*A_{vn}*f_u}{\sqrt{3}*\gamma_{mo}}\\')) -# shear_rup_eqn.append(NoEscape(r'&= 0.9 *(' + h + '-(' + n_r + '*' + d_o + '))*' + t + '*' + f_u + r'\\')) -# shear_rup_eqn.append(NoEscape(r'&=' + v_dn + '\end{aligned}')) -# return shear_rup_eqn -# def shear_capacity_prov(V_dy, V_dn, V_db): -# V_d = min(V_dy,V_dn,V_db) -# V_d = str(V_d) -# V_dy = str(V_dy) -# V_dn = str(V_dn) -# V_db = str(V_db) -# shear_capacity_eqn = Math(inline=True) -# shear_capacity_eqn.append(NoEscape(r'\begin{aligned} V_d &= Min(V_{dy},V_{dn},V_{db})\\')) -# shear_capacity_eqn.append(NoEscape(r'&= Min('+V_dy+','+V_dn+','+V_db+r')\\')) -# shear_capacity_eqn.append(NoEscape(r'&='+V_d + '\end{aligned}')) -# return shear_capacity_eqn - -# def shear_Rupture_prov(h, t, fu,v_dn): #weld -# h = str(h) -# t = str(t) -# -# f_u = str(fu) -# v_dn = str(v_dn) -# -# shear_rup_eqn = Math(inline=True) -# shear_rup_eqn.append(NoEscape(r'\begin{aligned} V_{dn} &= \frac{0.9*A_{vn}*f_u}{\sqrt{3}*\gamma_{mo}}\\')) -# shear_rup_eqn.append(NoEscape(r'&=(0.9*'+h+'*'+t+'*'+f_u+r'\\')) -# shear_rup_eqn.append(NoEscape(r'&=' + v_dn + '\end{aligned}')) -# return shear_rup_eqn - - -def tension_yield_prov(l,t, f_y, gamma, T_dg): - l = str(l) - t = str(t) - f_y = str(f_y) - gamma = str(gamma) - T_dg = str(T_dg) - tension_yield_eqn = Math(inline=True) - tension_yield_eqn.append(NoEscape(r'\begin{aligned} T_{dg} &= \frac{Depth*t_p*f_y}{\gamma_{mo}}\\')) - tension_yield_eqn.append(NoEscape(r'&=\frac{'+l+'*'+t+'*'+f_y+'}{'+gamma+r'}\\')) - tension_yield_eqn.append(NoEscape(r'&=' + T_dg + '\end{aligned}')) - return tension_yield_eqn - - -def height_of_flange_cover_plate(B,sp,b_fp): #weld - B = str(B) - sp = str(sp) - b_fp = str (b_fp) - height_for_flange_cover_plate_eqn =Math(inline=True) - - height_for_flange_cover_plate_eqn.append(NoEscape(r'\begin{aligned} B_{fp} &= {B - 2*sp} \\')) - height_for_flange_cover_plate_eqn.append(NoEscape(r'&= {' + B + ' - 2 * ' + sp + r'} \\')) - - height_for_flange_cover_plate_eqn.append(NoEscape(r'&=' + b_fp + '\end{aligned}')) - return height_for_flange_cover_plate_eqn - - -def height_of_web_cover_plate(D,sp,b_wp,T,R_1): #weld - D = str(D) - sp = str(sp) - b_wp = str (b_wp) - R_1 = str(R_1) - T= str(T) - height_for_web_cover_plate_eqn =Math(inline=True) - - height_for_web_cover_plate_eqn.append(NoEscape(r'\begin{aligned} W_{wp} &= {D-2*T -(2 * R1)- 2*sp} \\')) - height_for_web_cover_plate_eqn.append(NoEscape(r'&= {' + D + ' - 2 * ' +T+'- (2 *'+ R_1+')- 2 *'+ sp + r'} \\')) - - height_for_web_cover_plate_eqn.append(NoEscape(r'&=' + b_wp + '\end{aligned}')) - return height_for_web_cover_plate_eqn - - -def inner_plate_height_weld(B,sp,t,r_1, b_ifp):#weld - B = str(B) - sp = str(sp) - t = str (t) - r_1 = str(r_1) - b_ifp = str(b_ifp) - inner_plate_height_weld_eqn =Math(inline=True) - inner_plate_height_weld_eqn.append(NoEscape(r'\begin{aligned} B_{ifp} &= \frac{B - 4*sp - t- 2*R1}{2} \\')) - inner_plate_height_weld_eqn.append(NoEscape(r'&= \frac{'+B +'- 4*'+sp+'-' +t+ '- 2*'+r_1+r'} {2} \\')) - inner_plate_height_weld_eqn.append(NoEscape(r'&=' + b_ifp + '\end{aligned}')) - return inner_plate_height_weld_eqn - - -def plate_Length_req(l_w,t_w,g,l_fp,conn =None): #weld - l_w = str(l_w) - t_w = str (t_w) - g = str (g) - l_fp = str(l_fp) - min_plate_Length_eqn = Math(inline=True) - if conn =="Flange": - min_plate_Length_eqn.append(NoEscape(r'\begin{aligned} L_{fp} & = [2*(l_{w} + 2*t_w) + g]\\')) - min_plate_Length_eqn.append(NoEscape(r'&= [2*('+ l_w +'+2*'+t_w+') +' + g+ r']\\')) - min_plate_Length_eqn.append(NoEscape(r'&=' + l_fp + '\end{aligned}')) - else: - min_plate_Length_eqn.append(NoEscape(r'\begin{aligned} L_{wp} & = [2*(l_{w} + 2*t_w) + g]\\')) - min_plate_Length_eqn.append(NoEscape(r'&= [2*(' + l_w + '+2*' + t_w + ') +' + g + r']\\')) - min_plate_Length_eqn.append(NoEscape(r'&=' + l_fp + '\end{aligned}')) - - return min_plate_Length_eqn - - -def flange_weld_stress(F_f,l_eff,F_ws): - l_eff = str(l_eff) - F_ws = str(F_ws) - F_f =str(F_f) - flange_weld_stress_eqn = Math(inline=True) - flange_weld_stress_eqn.append(NoEscape(r'\begin{aligned} Stress &= \frac{F_f*1000}{l_{eff}}}\\')) - flange_weld_stress_eqn.append(NoEscape(r' &= \frac{' + F_f + '*1000}{' + l_eff + r'}\\')) - flange_weld_stress_eqn.append(NoEscape(r'&= ' + F_ws + r'\end{aligned}')) - - return flange_weld_stress_eqn - - -def tension_yield_prov(l,t, f_y, gamma, T_dg,multiple =1): - l = str(l) - t = str(t) - f_y = str(f_y) - gamma = str(gamma) - multiple = str(multiple) - T_dg = str(T_dg) - tension_yield_eqn = Math(inline=True) - tension_yield_eqn.append(NoEscape(r'\begin{aligned} T_{dg} &= \frac{l*t*f_y}{\gamma_{mo}}\\')) - tension_yield_eqn.append(NoEscape(r'&=\frac{'+multiple+'*'+l+'*'+t+'*'+f_y+'}{'+gamma+r'}\\')) - tension_yield_eqn.append(NoEscape(r'&=' + T_dg + '\end{aligned}')) - return tension_yield_eqn - - -def tension_rupture_bolted_prov(w_p, t_p, n_c, d_o, fu,gamma_m1,T_dn,multiple=1): - w_p = str(w_p) - t_p = str(t_p) - n_c = str(n_c) - d_o = str(d_o) - f_u = str(fu) - T_dn = str(T_dn) - gamma_m1 = str(gamma_m1) - multiple = str(multiple) - Tensile_rup_eqnb = Math(inline=True) - Tensile_rup_eqnb.append(NoEscape(r'\begin{aligned} T_{dn} &= \frac{0.9*A_{n}*f_u}{\gamma_{m1}}\\')) - Tensile_rup_eqnb.append(NoEscape(r'&=\frac{'+multiple+'*0.9* ('+ w_p + '-' + n_c +'*'+ d_o + ')*' + t_p + '*' + f_u + r'}{' + gamma_m1 + r'}\\')) - Tensile_rup_eqnb.append(NoEscape(r'&=' + T_dn + '\end{aligned}')) - return Tensile_rup_eqnb - - -def tension_rupture_welded_prov(w_p, t_p, fu,gamma_m1,T_dn,multiple =1): - w_p = str(w_p) - t_p = str(t_p) - f_u = str(fu) - T_dn = str(T_dn) - multiple = str(multiple) - T_dn = str(T_dn) - gamma_m1 = str(gamma_m1) - Tensile_rup_eqnw = Math(inline=True) - Tensile_rup_eqnw.append(NoEscape(r'\begin{aligned} T_{dn} &= \frac{0.9*A_{n}*f_u}{\gamma_{m1}}\\')) - # Tensile_rup_eqnw.append(NoEscape(r'&=\frac{0.9*'+w_p+'*'+t_p+'*'+f_u+'}{'+gamma_m1+r'}\\')) - Tensile_rup_eqnw.append(NoEscape(r'&=\frac{' + multiple + '*0.9*' + w_p + '*' + t_p + '*' + f_u + '}{' + gamma_m1 + r'}\\')) - Tensile_rup_eqnw.append(NoEscape(r'&=' + T_dn +'\end{aligned}')) - return Tensile_rup_eqnw - - -def tensile_capacity_prov(T_dg, T_dn, T_db =0.0): - - tension_capacity_eqn = Math(inline=True) - if T_db != 0.0: - T_d = min(T_dg,T_dn,T_db) - T_d = str(T_d) - T_dg = str(T_dg) - T_dn = str(T_dn) - T_db = str(T_db) - tension_capacity_eqn.append(NoEscape(r'\begin{aligned} T_d &= min(T_{dg},T_{dn},T_{db})\\')) - tension_capacity_eqn.append(NoEscape(r'&= min(' + T_dg + ',' + T_dn + ',' + T_db + r')\\')) - else: - T_d = min(T_dg, T_dn) - T_dg = str(T_dg) - T_dn = str(T_dn) - T_d = str(T_d) - tension_capacity_eqn.append(NoEscape(r'\begin{aligned} T_d &= min(T_{dg},T_{dn})\\')) - tension_capacity_eqn.append(NoEscape(r'&= min(' + T_dg + ',' + T_dn + r')\\')) - tension_capacity_eqn.append(NoEscape(r'&='+ T_d + r'\end{aligned}')) - return tension_capacity_eqn - - -def spacing (sp,t_w): - - # sp = max(15,s+5) - sp = str(sp) - t_w = str(t_w) - space_provided_eqn = Math(inline=True) - space_provided_eqn.append(NoEscape(r'\begin{aligned} sp &= max(15,(t_w+5))\\')) - space_provided_eqn.append(NoEscape(r'&= max(15,('+t_w+ r'+5))\\')) - space_provided_eqn.append(NoEscape(r'&=' + sp + r'\end{aligned}')) - return space_provided_eqn - - -def throat_thickness_req(t,t_t): - t_t <= .7*t - t_t >= 3 - t = str(t) - t_t = str(t_t) - throat_thickness_eqn = Math(inline=True) - throat_thickness_eqn.append(NoEscape(r'\begin{aligned} [t_t& <= .7*t ]; [t_t& = >= 3]\\')) - throat_thickness_eqn.append(NoEscape(r'&='+ t_t+ '\end{aligned}')) - return throat_thickness_eqn - - -def height_of_inner_flange_cover_plate(b_fp,B,t_w,r_r,sp): - # sp= max(15,s+5) - b_fp =str(b_fp) - B = str(B) - t_w =str(t_w) - r_r = str(r_r) - sp = str(sp) - #s = str(s) - ht_inner_flange_cover_plate_eqn = Math(inline=True) - ht_inner_flange_cover_plate_eqn.append(NoEscape(r'\begin{aligned} b_fp &= \frac{B-t_w -2*r_r -4*sp}{2}\\')) - ht_inner_flange_cover_plate_eqn.append(NoEscape(r'&= \frac{'+B+'-'+t_w+'-2' +'*'+r_r+' -4' + '*' +sp +r'}{2}\\')) - ht_inner_flange_cover_plate_eqn.append(NoEscape(r'&= ' + b_fp+ r'\end{aligned}')) - return ht_inner_flange_cover_plate_eqn - - -def mom_axial_IR_prov(M,M_d,N,N_d,IR): - M = str(M) - M_d = str(M_d) - N = str(N) - N_d = str(N_d) - IR = str(IR) - mom_axial_IR_eqn = Math(inline=True) - mom_axial_IR_eqn.append(NoEscape(r'\begin{aligned} \frac{'+M+'}{'+M_d+r'}+\frac{'+N+'}{'+N_d+'}='+IR+r'\end{aligned}')) - return mom_axial_IR_eqn - - -def IR_req(IR): - IR = str(IR) - IR_req_eqn = Math(inline=True) - IR_req_eqn.append(NoEscape(r'\begin{aligned} \leq'+IR+'\end{aligned}')) - return IR_req_eqn - - -def min_weld_size_req(conn_plates_weld,min_weld_size): - - t1 = str(conn_plates_weld[0]) - t2 = str(conn_plates_weld[1]) - tmax = str(max(conn_plates_weld)) - weld_min = str(min_weld_size) - - min_weld_size_eqn = Math(inline=True) - min_weld_size_eqn.append(NoEscape(r'\begin{aligned} &Thickness~of~Thicker~part\\')) - min_weld_size_eqn.append(NoEscape(r'\noindent &=max('+t1+','+t2+r')\\')) - min_weld_size_eqn.append(NoEscape(r'&='+tmax+r'\\')) - min_weld_size_eqn.append(NoEscape(r'&IS800:2007~cl.10.5.2.3~Table 21,\\')) - min_weld_size_eqn.append(NoEscape(r' &t_{w_{min}}=' + weld_min + r'\end{aligned}')) - return min_weld_size_eqn - - -def min_weld_size_req_01(conn_plates_weld, red, min_weld_size): - # t1 = str(conn_plates_weld[0]) - # t2 = str(conn_plates_weld[0]) - tmax = min(conn_plates_weld) - tmin = int (tmax - red) - tmin = str(tmin) - tmax= str(int(tmax)) - weld_min = str(min_weld_size) - - min_weld_size_eqn = Math(inline=True) - min_weld_size_eqn.append(NoEscape(r'\begin{aligned} & t_{w_{min}}~based~on~thinner~part\\')) - min_weld_size_eqn.append(NoEscape(r'& ='+tmax+ '~or~' +tmin+ r'\\')) - min_weld_size_eqn.append(NoEscape(r'& IS800:2007~cl.10.5.2.3~Table 21\\' )) - min_weld_size_eqn.append(NoEscape(r'& t_{w_{min}}~based~on~thicker~part=' + weld_min + r'\end{aligned}')) - return min_weld_size_eqn - - -def max_weld_size_req(conn_plates_weld,max_weld_size): - t1 = str(conn_plates_weld[0]) - t2 = str(conn_plates_weld[1]) - t_min = str(min(conn_plates_weld)) - weld_max = str(max_weld_size) - - max_weld_size_eqn = Math(inline=True) - max_weld_size_eqn.append(NoEscape(r'\begin{aligned} & Thickness~of~Thinner~part\\')) - max_weld_size_eqn.append(NoEscape(r'&=min('+t1+','+t2+r')='+t_min+r'\\')) - max_weld_size_eqn.append(NoEscape(r'&t_{w_{max}} =' + weld_max + r'\end{aligned}')) - return max_weld_size_eqn - - -def weld_strength_req(V,A,M,Ip_w,y_max,x_max,l_w,R_w): - T_wh = str(round(M * y_max/Ip_w,2)) - T_wv = str(round(M * x_max/Ip_w,2)) - V_wv = str(round(V /l_w,2)) - A_wh = str(round(A/l_w,2)) - - V = str(V) - A = str(A) - M = str(M) - Ip_w = str(Ip_w) - y_max = str(y_max) - x_max = str(x_max) - l_w = str(l_w) - R_w = str(R_w) - weld_stress_eqn = Math(inline=True) - weld_stress_eqn.append(NoEscape(r'\begin{aligned} R_w&=\sqrt{(T_{wh}+A_{wh})^2 + (T_{wv}+V_{wv})^2}\\')) - weld_stress_eqn.append(NoEscape(r'T_{wh}&=\frac{M*y_{max}}{I{pw}}=\frac{'+M+'*'+y_max+'}{'+Ip_w+r'}\\')) - weld_stress_eqn.append(NoEscape(r'T_{wv}&=\frac{M*x_{max}}{I{pw}}=\frac{'+M+'*'+x_max+'}{'+Ip_w+r'}\\')) - weld_stress_eqn.append(NoEscape(r'V_{wv}&=\frac{V}{l_w}=\frac{'+V+'}{'+l_w+r'}\\')) - weld_stress_eqn.append(NoEscape(r'A_{wh}&=\frac{A}{l_w}=\frac{'+A+'}{'+l_w+r'}\\')) - weld_stress_eqn.append(NoEscape(r'R_w&=\sqrt{('+T_wh+'+'+A_wh+r')^2 + ('+T_wv+'+'+V_wv+r')^2}\\')) - weld_stress_eqn.append(NoEscape(r'&='+R_w+r'\end{aligned}')) - - return weld_stress_eqn - - -def weld_strength_stress(V_u,A_w,M_d,Ip_w,y_max,x_max,l_eff,R_w): - T_wh = str(round(M_d * y_max/Ip_w,2)) - T_wv = str(round(M_d * x_max/Ip_w,2)) - V_wv = str(round(V_u /l_eff,2)) - A_wh = str(round(A_w/l_eff,2)) - - V_u= str(V_u) - A_w = str(A_w) - M_d= str(M_d) - Ip_w = str(Ip_w) - y_max = str(y_max) - x_max = str(x_max) - l_eff = str(l_eff) - R_w = str(R_w) - weld_stress_eqn = Math(inline=True) - weld_stress_eqn.append(NoEscape(r'\begin{aligned} R_w&=\sqrt{(T_{wh}+A_{wh})^2 + (T_{wv}+V_{wv})^2}\\')) - weld_stress_eqn.append(NoEscape(r'T_{wh}&=\frac{M_d*y_{max}}{I{pw}}\\')) - weld_stress_eqn.append(NoEscape(r'&=\frac{'+M_d+'*'+y_max+'}{'+Ip_w+r'}\\')) - weld_stress_eqn.append(NoEscape(r'T_{wv}&=\frac{M_d* x_{max}}{I{pw}}\\')) - weld_stress_eqn.append(NoEscape(r'&=\frac{'+M_d+'*'+x_max+'}{'+Ip_w+r'}\\')) - weld_stress_eqn.append(NoEscape(r'V_{wv}&=\frac{V_u}{l_{eff}}\\ ')) - weld_stress_eqn.append(NoEscape(r'&=\frac{'+V_u+'}{'+l_eff+r'}\\')) - weld_stress_eqn.append(NoEscape(r'A_{wh}&=\frac{A_u}{l_{eff}}\\')) - weld_stress_eqn.append(NoEscape(r'&=\frac{'+A_w+'}{'+l_eff+r'}\\')) - weld_stress_eqn.append(NoEscape(r'R_w&=\sqrt{('+T_wh+'+'+A_wh+r')^2 + ('+T_wv+'+'+V_wv+r')^2}\\')) - weld_stress_eqn.append(NoEscape(r'&='+R_w+r'\end{aligned}')) - - return weld_stress_eqn - - -def weld_strength_prov(conn_plates_weld_fu,gamma_mw,t_t,f_w): - - f_u = str(min(conn_plates_weld_fu)) - t_t = str(t_t) - gamma_mw = str(gamma_mw) - f_w = str(f_w) - weld_strength_eqn = Math(inline=True) - weld_strength_eqn.append(NoEscape(r'\begin{aligned} f_w &=\frac{t_t*f_u}{\sqrt{3}*\gamma_{mw}}\\')) - weld_strength_eqn.append(NoEscape(r'&=\frac{'+t_t+'*'+f_u+'}{\sqrt{3}*'+ gamma_mw+r'}\\')) - weld_strength_eqn.append(NoEscape(r'&='+f_w+r'\end{aligned}')) - - return weld_strength_eqn - - -def axial_capacity(area,fy, gamma_m0,axial_capacity): #todo Done - """ - Calculate axial capacity of member - Args: - area: Gross area of member in mm square (float) - fy:Yeilding strength of material in N/mm square (float) - gamma_m0: IS800_2007.cl_5_4_1_Table_5['gamma_m0'] (float) - axial_capacity: Axial capacity of member in mm square (float) - Returns: - Axial capacity of member - Note: - Reference: - IS 800:2007, cl 10.7 - - """ - area = str(area) - fy=str(fy) - gamma_m0=str(gamma_m0) - axial_capacity = str(axial_capacity) - axial_capacity_eqn = Math(inline=True) - axial_capacity_eqn.append(NoEscape(r'\begin{aligned} A_c &=\frac{A*f_y}{\gamma_{m0} *10^3}\\')) - axial_capacity_eqn.append(NoEscape(r'&=\frac{'+area+'*'+fy+'}{'+ gamma_m0+r'* 10^3}\\')) - axial_capacity_eqn.append(NoEscape(r'&=' + axial_capacity + r'\\')) - axial_capacity_eqn.append(NoEscape(r'&[Ref.~IS~800:2007,~Cl.~10.7]\end{aligned}')) - return axial_capacity_eqn - - -def min_max_axial_capacity(axial_capacity,min_ac): #todo anjali - min_ac = str(min_ac) - axial_capacity = str(axial_capacity) - min_ac_eqn = Math(inline=True) - min_ac_eqn.append(NoEscape(r'\begin{aligned} Ac_{min} &= 0.3 * A_c\\')) - min_ac_eqn.append(NoEscape(r'&= 0.3 *' + axial_capacity + r'\\')) - min_ac_eqn.append(NoEscape(r'&=' + min_ac + r'\\')) - min_ac_eqn.append(NoEscape(r'Ac_{max} &= Ac \\')) - min_ac_eqn.append(NoEscape(r'&=' +axial_capacity+ r'\end{aligned}')) - return min_ac_eqn - - -def ir_sum_bb_cc(Al, M , A_c ,M_c,IR_axial ,IR_moment ,sum_IR): - IR_axial = str(IR_axial) - IR_moment = str(IR_moment) - Al = str(Al) - M = str(M) - A_c = str(A_c) - M_c = str(M_c) - sum_IR =str(sum_IR) - ir_sum_bb_cc_eqn = Math(inline=True) - ir_sum_bb_cc_eqn.append(NoEscape(r'\begin{aligned} IR ~axial~~~~&= A_l / A_c \\')) - ir_sum_bb_cc_eqn.append(NoEscape(r'& ='+ Al +'/'+ A_c+r' \\')) - ir_sum_bb_cc_eqn.append(NoEscape(r'& ='+ IR_axial +r' \\')) - - ir_sum_bb_cc_eqn.append(NoEscape(r'IR ~moment &= M / M_c \\')) - ir_sum_bb_cc_eqn.append(NoEscape(r'& =' + M + '/' + M_c + r' \\')) - ir_sum_bb_cc_eqn.append(NoEscape(r'& =' + IR_moment + r' \\')) - - ir_sum_bb_cc_eqn.append(NoEscape(r'IR ~sum~~~~~ &= IR ~axial + IR ~moment \\')) - ir_sum_bb_cc_eqn.append(NoEscape(r'&= '+ IR_axial +'+ '+IR_moment + r' \\')) - ir_sum_bb_cc_eqn.append(NoEscape(r'& =' + sum_IR + r'\end{aligned}')) - return ir_sum_bb_cc_eqn - -def min_loads_required(conn): - min_loads_required_eqn= Math(inline=True) - min_loads_required_eqn.append(NoEscape(r'\begin{aligned} &if~~ IR ~axial < 0.3 ~and~ IR ~moment < 0.5 \\')) - min_loads_required_eqn.append(NoEscape(r' &~~~Ac_{min} = 0.3 * A_c\\')) - min_loads_required_eqn.append(NoEscape(r' &~~~Mc_{min}= 0.5 * M_c\\')) - if conn =="beam_beam": - min_loads_required_eqn.append(NoEscape(r' &elif~~ sum ~IR <= 1.0 ~and~ IR ~moment < 0.5\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~if~~ (0.5 - IR ~moment) < (1 - sum ~IR)\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~~~~Mc_{min} = 0.5 * M_c\\')) - min_loads_required_eqn.append(NoEscape(r'& ~~~else\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~~~~Mc_{min} = M + ((1 - sum ~IR) * M_c)\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~Ac_{min} = Al \\')) - - min_loads_required_eqn.append(NoEscape(r'&elif~~ sum ~IR <= 1.0~ and~ IR ~axial < 0.3\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~if~~ (0.3 - IR ~axial) < (1 - sum ~IR)\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~~~~Ac_{min} = 0.3 * A_c\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~else~~\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~~~~Ac_{min} = Al + ((1 - sum ~IR) * A_c)\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~Mc_{min} = M \\')) - else: - min_loads_required_eqn.append(NoEscape(r'&elif~~ sum ~IR <= 1.0~ and~ IR ~axial < 0.3\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~if~~ (0.3 - IR ~axial) < (1 - sum ~IR)\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~~~~Ac_{min} = 0.3 * A_c\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~else~~\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~~~~Ac_{min} = Al + ((1 - sum ~IR) * A_c)\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~Mc_{min} = M \\')) - - min_loads_required_eqn.append(NoEscape(r' &elif~~ sum ~IR <= 1.0 ~and~ IR ~moment < 0.5\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~if~~ (0.5 - IR ~moment) < (1 - sum ~IR)\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~~~~Mc_{min} = 0.5 * M_c\\')) - min_loads_required_eqn.append(NoEscape(r'& ~~~else\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~~~~Mc_{min} = M + ((1 - sum ~IR) * M_c)\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~Ac_{min} = Al \\')) - - min_loads_required_eqn.append(NoEscape(r'&else~~\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~Ac_{min} = Al\\')) - min_loads_required_eqn.append(NoEscape(r'&~~~Mc_{min} = M \end{aligned}')) - return min_loads_required_eqn - -def min_loads_provided(min_ac,min_mc,conn): - min_ac = str(min_ac) - min_mc = str(min_mc) - - if conn == "beam_beam": - min_loads_provided_eqn = Math(inline=True) - min_loads_provided_eqn.append(NoEscape(r'\begin{aligned} & Mc_{min} =' + min_mc + r'\\')) - min_loads_provided_eqn.append(NoEscape(r'& Ac_{min} =' + min_ac + r'\end{aligned}')) - else: - min_loads_provided_eqn = Math(inline=True) - min_loads_provided_eqn.append(NoEscape(r'\begin{aligned} & Ac_{min} =' + min_ac + r'\\')) - min_loads_provided_eqn.append(NoEscape(r'& Mc_{min} =' + min_mc + r'\end{aligned}')) - return min_loads_provided_eqn - -def axial_capacity_req(axial_capacity,min_ac): - min_ac = str(min_ac) - axial_capacity = str(axial_capacity) - ac_req_eqn = Math(inline=True) - ac_req_eqn.append(NoEscape(r'\begin{aligned} Ac_{min} &= 0.3 * A_c\\')) - ac_req_eqn.append(NoEscape(r'&= 0.3 *' + axial_capacity + r'\\')) - ac_req_eqn.append(NoEscape(r'&=' + min_ac + r'\\')) - ac_req_eqn.append(NoEscape(r'Ac_{max} &=' +axial_capacity +r'\end{aligned}')) - return ac_req_eqn - - -def prov_axial_load(axial_input,min_ac,app_axial_load,axial_capacity): - min_ac = str(min_ac) - axial_input = str(axial_input) - app_axial_load = str(app_axial_load) - - axial_capacity = str(axial_capacity) - prov_axial_load_eqn = Math(inline=True) - # prov_axial_load_eqn.append(NoEscape(r'\begin{aligned} Ac_{min} &= 0.3 * A_c\\')) - # prov_axial_load_eqn.append(NoEscape(r'&= 0.3 *' + axial_capacity + r'\\')) - # prov_axial_load_eqn.append(NoEscape(r'&=' + min_ac + r'\\')) - - prov_axial_load_eqn.append(NoEscape(r'\begin{aligned} Au~~ &= max(A,Ac_{min} )\\')) - prov_axial_load_eqn.append(NoEscape(r'&= max( ' + axial_input + ',' + min_ac + r')\\')) - prov_axial_load_eqn.append(NoEscape(r'&=' + app_axial_load + r'\end{aligned}')) - return prov_axial_load_eqn - - -def shear_capacity(h, t,f_y, gamma_m0,shear_capacity): # same as #todo anjali - """ - Calculate factored design shear force in the section due to external actions - Args: - h:Height of the section - t:Thickness of the section - f_y: Yield strength of web - gamma_m0:1.1 (partial safety factor against shear failure) - shear_capacity:Factored design shear force - Returns: - Factored design shear force - Note: - Reference: - IS 800:2007, cl 8.4 - - """ - h = str(h) - t = str(t) - f_y = str(f_y) - gamma_m0 = str(gamma_m0) - shear_capacity = str(shear_capacity) - shear_capacity_eqn = Math(inline=True) - shear_capacity_eqn.append(NoEscape(r'\begin{aligned} S_c &= \frac{0.6*A_v*f_y}{\sqrt{3}*\gamma_{mo} *10^3}\\')) - shear_capacity_eqn.append(NoEscape(r'&=\frac{0.6*' + h + r'*' + t + r'*' + f_y + r'}{\sqrt{3}*' + gamma_m0 + r' *10^3}\\')) - shear_capacity_eqn.append(NoEscape(r'&=' + shear_capacity + r'\\')) - shear_capacity_eqn.append(NoEscape(r'&[Ref.~IS~800:2007,~Cl.~8.4]\end{aligned}')) - return shear_capacity_eqn - - -def min_max_shear_capacity(shear_capacity,min_sc): #todo anjali - min_sc = str(min_sc) - shear_capacity = str(shear_capacity) - min_sc_eqn = Math(inline=True) - - min_sc_eqn.append(NoEscape(r'\begin{aligned} Vc_{min} &= 0.6 * S_c\\')) - min_sc_eqn.append(NoEscape(r'&= 0.6 *' + shear_capacity +r'\\')) - min_sc_eqn.append(NoEscape(r'&=' + min_sc + r'\\')) - min_sc_eqn.append(NoEscape(r'Vc_{max} &= Sc \\')) - min_sc_eqn.append(NoEscape(r'&=' +shear_capacity+ r'\end{aligned}')) - return min_sc_eqn - - -def prov_shear_load(shear_input,min_sc,app_shear_load,shear_capacity_1): - min_sc = str(min_sc) - shear_input = str(shear_input) - app_shear_load = str(app_shear_load) - shear_capacity_1 = str(shear_capacity_1) - app_shear_load_eqn = Math(inline=True) - app_shear_load_eqn.append(NoEscape(r'\begin{aligned} Vc_{min} &= min(0.15 * S_c / 0.6, 40.0)\\')) - - app_shear_load_eqn.append(NoEscape(r'& = min(0.15 *'+ shear_capacity_1 +r'/ 0.6, 40.0)\\')) - app_shear_load_eqn.append(NoEscape(r'&=' + min_sc + r'\\')) - app_shear_load_eqn.append(NoEscape(r' Vu~~ &= max(V,Vc_{min})\\')) - app_shear_load_eqn.append(NoEscape(r'&= max(' + shear_input + ',' + min_sc + r')\\')) - app_shear_load_eqn.append(NoEscape(r'&=' + app_shear_load + r'\end{aligned}')) - return app_shear_load_eqn - - -def plastic_moment_capacty(beta_b, Z_p, f_y, gamma_m0 ,Pmc): # same as ##TODO:done - """ - Calculate member design moment capacity - Args: - - beta_b:1 for plastic and compact sections & Ze/Zp for semi compact section (int) - Z_p:Plastic section modulus of cross section mm^3 (float) - f_y:Yield stress of the material in N/mm square (float) - gamma_m0:partial safety factor (float) - Pmc:Plastic moment capacity in N-mm (float) - Returns: - Plastic moment capacity in N-mm (float) - - Note: - Reference: - IS 800:2007, cl 8.2.1.2 - - """ - beta_b = str(beta_b) - Z_p = str(Z_p) - f_y = str(f_y) - gamma_m0 =str(gamma_m0 ) - Pmc = str(Pmc) - Pmc_eqn = Math(inline=True) - Pmc_eqn.append(NoEscape(r'\begin{aligned} Pmc &= \frac{\beta_b * Z_p *fy}{\gamma_{mo} * 10^6}\\')) - Pmc_eqn.append(NoEscape(r'&=\frac{' + beta_b + r'*' +Z_p + r'*' + f_y + r'}{' + gamma_m0 + r' * 10^6}\\')) - Pmc_eqn.append(NoEscape(r'&=' + Pmc + r'\\')) - Pmc_eqn.append(NoEscape(r'&[Ref.~IS~800:2007,~Cl.~8.2.1.2]\end{aligned}')) - return Pmc_eqn - - -def moment_d_deformation_criteria(fy,Z_e,Mdc):#TODO:done - """ - Calculate moment deformation capacity - Args: - fy:Yield stress of the material in N/mm square (float) - Z_e:Elastic section modulus of cross section in mm^3 (float) - Mdc:Moment deformation capacity in N-mm (float) - Note: - Reference: - IS 800:2007, cl 8.2.1.2 - Returns: - moment deformation capacity - """ - fy = str(fy) - Z_e = str(Z_e) - Mdc =str(Mdc) - Mdc_eqn= Math(inline=True) - Mdc_eqn.append(NoEscape(r'\begin{aligned} Mdc &= \frac{1.5 *Z_e *fy}{1.1* 10^6}\\')) - Mdc_eqn.append(NoEscape(r'&= \frac{1.5 *'+Z_e + '*' +fy +r'}{1.1* 10^6}\\')) - - Mdc_eqn.append(NoEscape(r'&= ' + Mdc+ r'\\')) - Mdc_eqn.append(NoEscape(r'&[Ref~IS~800:2007,~Cl.~8.2.1.2]\end{aligned}')) - return Mdc_eqn - - -def moment_capacity (Pmc , Mdc, M_c):#TODO:done - """ - Calculate moment capacity of the section - Args: - Pmc:Plastic moment capacity of the member in N-mm (float) - Mdc:Moment deformation capacity of the member in N-mm (float) - M_c: Moment capacity of the section in N-mm (float) - Returns: - moment capacity of the section - Note: - Reference: - IS 800:2007, cl 8.2.1.2 - - - """ - Pmc = str(Pmc) - Mdc =str(Mdc) - M_c = str (M_c) - M_c_eqn = Math(inline=True) - M_c_eqn.append(NoEscape(r'\begin{aligned} M_c &= min(Pmc,Mdc)\\')) - M_c_eqn.append(NoEscape(r'&=min('+Pmc+','+Mdc+ r')\\')) - M_c_eqn.append(NoEscape(r'&=' + M_c + r'\end{aligned}')) - M_c_eqn.append(NoEscape(r'&[Ref.~IS~800:2007,~Cl.~8.2.1.2]\end{aligned}')) - return M_c_eqn - - -def min_max_moment_capacity(moment_capacity,min_mc): #todo anjali - min_mc = str(min_mc) - moment_capacity = str(moment_capacity) - min_mc_eqn = Math(inline=True) - min_mc_eqn.append(NoEscape(r'\begin{aligned} Mc_{min} &= 0.5 * M_c\\')) - min_mc_eqn.append(NoEscape(r'&= 0.5 *' + moment_capacity +r'\\')) - min_mc_eqn.append(NoEscape(r'&=' + min_mc + r'\\')) - min_mc_eqn.append(NoEscape(r' Mc_{max} &= Mc \\')) - min_mc_eqn.append(NoEscape(r'&=' +moment_capacity+ r'\end{aligned}')) - return min_mc_eqn - - -def prov_moment_load(moment_input,min_mc,app_moment_load,moment_capacity): - min_mc = str(min_mc) - moment_input = str(moment_input) - app_moment_load = str(app_moment_load) - moment_capacity = str(moment_capacity) - app_moment_load_eqn = Math(inline=True) - # app_moment_load_eqn.append(NoEscape(r'\begin{aligned} Mc_{min} &= 0.5 * M_c\\')) - # app_moment_load_eqn.append(NoEscape(r'&= 0.5 *' + moment_capacity + r'\\')) - # app_moment_load_eqn.append(NoEscape(r'&=' + min_mc + r'\\')) - app_moment_load_eqn.append(NoEscape(r' \begin{aligned} Mu &= max(M,Mc_{min} )\\')) - app_moment_load_eqn.append(NoEscape(r'&= max(' + moment_input + r',' + min_mc + r')\\')) - app_moment_load_eqn.append(NoEscape(r'&=' + app_moment_load + r'\end{aligned}')) - return app_moment_load_eqn - - -def shear_rupture_prov_beam(h, t, n_r, d_o, fu,v_dn,gamma_m1,multiple=1): - h = str(h) - t = str(t) - n_r = str(n_r) - d_o = str(d_o) - f_u = str(fu) - v_dn = str(v_dn) - gamma_m1 = str(gamma_m1) - multiple = str(multiple) - shear_rup_eqn = Math(inline=True) - shear_rup_eqn.append(NoEscape(r'\begin{aligned} V_{dn} &= \frac{0.75*A_{vn}*f_u}{\sqrt{3}*\gamma_{m1}}\\')) - shear_rup_eqn.append(NoEscape(r'&= \frac{'+ multiple+'*0.75 *('+h+'-('+n_r+'*'+d_o+'))*'+t+'*'+f_u+ '}{\sqrt{3}*'+gamma_m1+ r'}\\')) - shear_rup_eqn.append(NoEscape(r'&=' + v_dn + '\end{aligned}')) - return shear_rup_eqn - - -def shear_Rupture_prov_weld(h, t,fu,v_dn,gamma_m1,multiple =1): #weld - h = str(h) - t = str(t) - gamma_m1 = str(gamma_m1) - f_u = str(fu) - v_dn = str(v_dn) - multiple = str(multiple) - - shear_rup_eqn = Math(inline=True) - shear_rup_eqn.append(NoEscape(r'\begin{aligned} V_{dn} &= \frac{0.75*A_{vn}*f_u}{\sqrt{3}*\gamma_{m1}}\\')) - shear_rup_eqn.append(NoEscape(r'&=\frac{'+ multiple+'*0.75*'+h+'*'+t+'*'+f_u+'}{\sqrt{3}*' +gamma_m1+ r'}\\')) - shear_rup_eqn.append(NoEscape(r'&=' + v_dn + '\end{aligned}')) - return shear_rup_eqn - - -def shear_capacity_prov(V_dy, V_dn, V_db = 0.0): - shear_capacity_eqn = Math(inline=True) - if V_db != 0.0: - V_d = min(V_dy,V_dn,V_db) - V_d = str(V_d) - V_dy = str(V_dy) - V_dn = str(V_dn) - V_db = str(V_db) - shear_capacity_eqn.append(NoEscape(r'\begin{aligned} V_d &= min(V_{dy},V_{dn},V_{db})\\')) - shear_capacity_eqn.append(NoEscape(r'&= min('+V_dy+','+V_dn+','+V_db+r')\\')) - elif V_db == 0.0 and V_dn == 0.0: - V_d = V_dy - V_d = str(V_d) - V_dy = str(V_dy) - shear_capacity_eqn.append(NoEscape(r'\begin{aligned} V_d &= V_{dy}\\')) - # shear_capacity_eqn.append(NoEscape(r'&=' + V_dy + r'\\')) - else: - V_d = min(V_dy, V_dn) - V_d = str(V_d) - V_dy = str(V_dy) - V_dn = str(V_dn) - shear_capacity_eqn.append(NoEscape(r'\begin{aligned} V_d &= min(V_{dy},V_{dn})\\')) - shear_capacity_eqn.append(NoEscape(r'&= min(' + V_dy + ',' + V_dn + r')\\')) - - shear_capacity_eqn.append(NoEscape(r'&='+V_d + '\end{aligned}')) - return shear_capacity_eqn - - -def get_pass_fail(required, provided,relation='greater'): - required = float(required) - provided = float(provided) - if provided==0: - return 'N/A' - else: - if relation == 'greater': - if required > provided: - return 'Pass' - else: - return 'Fail' - elif relation == 'geq': - if required >= provided: - return 'Pass' - else: - return 'Fail' - elif relation == 'leq': - if required <= provided: - return 'Pass' - else: - return 'Fail' - else: - if required < provided: - return 'Pass' - else: - return 'Fail' - - -def get_pass_fail2(min, provided, max): - min = float(min) - provided = float(provided) - max = float(max) - if provided == 0: - return 'N/A' - else: - if max >= provided and min <= provided: - return 'Pass' - else: - return 'Fail' - # elif relation == 'geq': - - -def min_prov_max(min, provided,max): - min = float(min) - provided = float(provided) - max = float(max) - if provided==0: - return 'N/A' - else: - if max >= provided and min<=provided: - return 'Pass' - else: - return 'Fail' - - -def member_yield_prov(Ag, fy, gamma_m0, member_yield,multiple = 1): - Ag = str(round(Ag,2)) - fy = str(fy) - gamma_m0 = str(gamma_m0) - multiple = str(multiple) - member_yield = str(member_yield) - member_yield_eqn = Math(inline=True) - member_yield_eqn.append(NoEscape(r'\begin{aligned}T_{dg}~or~A_c&= \frac{'+ multiple + r' * A_g ~ f_y}{\gamma_{m0}}\\')) - member_yield_eqn.append(NoEscape(r'&= \frac{'+ multiple + '*' + Ag + '*' + fy + '}{'+ gamma_m0 + r'}\\')) - member_yield_eqn.append(NoEscape(r'&= ' + member_yield + r'\end{aligned}')) - return member_yield_eqn - - -def member_rupture_prov(A_nc, A_go, F_u, F_y, L_c, w, b_s, t,gamma_m0,gamma_m1,beta,member_rup,multiple = 1): - w = str(w) - t = str(t) - fy = str(F_y) - fu = str(F_u) - b_s = str(b_s) - L_c = str(L_c) - A_nc = str(A_nc) - A_go = str(A_go) - gamma_m0 = str(gamma_m0) - gamma_m1 = str(gamma_m1) - beta = str(round(beta,2)) - member_rup = str(member_rup) - multiple = str(multiple) - member_rup_eqn = Math(inline=True) - member_rup_eqn.append(NoEscape(r'\begin{aligned}\beta &= 1.4 - 0.076*\frac{w}{t}*\frac{f_{y}}{0.9*f_{u}}*\frac{b_s}{L_c}\\')) - member_rup_eqn.append(NoEscape(r'&\leq\frac{0.9*f_{u}*\gamma_{m0}}{f_{y}*\gamma_{m1}} \geq 0.7 \\')) - member_rup_eqn.append(NoEscape(r'&= 1.4 - 0.076*\frac{'+ w +'}{'+ t + r'}*\frac{'+ fy +'}{0.9*'+ fu + r'}*\frac{'+ b_s +'}{' + L_c + r' }\\')) - member_rup_eqn.append(NoEscape(r'&\leq\frac{0.9* '+ fu + '*'+ gamma_m0 +'}{' +fy+'*'+gamma_m1 + r'} \geq 0.7 \\')) - member_rup_eqn.append(NoEscape(r'&= '+ beta + r'\\')) - member_rup_eqn.append(NoEscape(r'T_{dn} &= '+multiple+'*' r'(\frac{0.9*A_{nc}*f_{u}}{\gamma_{m1}} + \frac{\beta * A_{go} * f_{y}}{\gamma_{m0}})\\')) - member_rup_eqn.append(NoEscape(r'&= '+multiple+ r'*(\frac{0.9* '+ A_nc +'*' + fu + '}{'+ gamma_m1 + r'} + \frac{' + beta + '*' + A_go + '*' + fy + '}{' + gamma_m0 + r'})\\')) - member_rup_eqn.append(NoEscape(r'&= '+ member_rup + r'\end{aligned}')) - - return member_rup_eqn - - -def flange_weld_stress(F_f,l_eff,F_ws): - F_f = str(F_f) - l_eff = str(l_eff) - F_ws = str(F_ws) - flange_weld_stress_eqn = Math(inline=True) - flange_weld_stress_eqn.append(NoEscape(r'\begin{aligned} Stress &= \frac{F_f*10^3}{l_{eff}}\\')) - flange_weld_stress_eqn.append(NoEscape(r' &= \frac{'+F_f+'*10^3}{'+l_eff+ r'}\\')) - flange_weld_stress_eqn.append(NoEscape(r'&= ' + F_ws+ r'\end{aligned}')) - - return flange_weld_stress_eqn - - -def blockshear_prov(Tdb,A_vg = None, A_vn = None, A_tg = None, A_tn = None, f_u = None, f_y = None ,gamma_m0 = None ,gamma_m1 = None,stress=None): - Tdb = str(Tdb) - A_vg = str(A_vg) - A_vn = str(A_vn) - A_tg = str(A_tg) - A_tn = str(A_tn) - f_y = str(f_y) - f_u = str(f_u) - gamma_m1 = str(gamma_m1) - gamma_m0 = str(gamma_m0) - - member_block_eqn = Math(inline=True) - if stress == "shear": - member_block_eqn.append(NoEscape(r'\begin{aligned}V_{db1} &= \frac{A_{vg} f_{y}}{\sqrt{3} \gamma_{m0}} + \frac{0.9 A_{tn} f_{u}}{\gamma_{m1}}\\')) - member_block_eqn.append(NoEscape(r'V_{db2} &= \frac{0.9*A_{vn} f_{u}}{\sqrt{3} \gamma_{m1}} + \frac{A_{tg} f_{y}}{\gamma_{m0}}\\')) - member_block_eqn.append(NoEscape(r'V_{db} &= min(V_{db1}, V_{db2})= ' + Tdb + r'\end{aligned}')) - else: - member_block_eqn.append(NoEscape(r'\begin{aligned}T_{db1} &= \frac{A_{vg} f_{y}}{\sqrt{3} \gamma_{m0}} + \frac{0.9 A_{tn} f_{u}}{\gamma_{m1}}\\')) - member_block_eqn.append(NoEscape(r'T_{db2} &= \frac{0.9*A_{vn} f_{u}}{\sqrt{3} \gamma_{m1}} + \frac{A_{tg} f_{y}}{\gamma_{m0}}\\')) - member_block_eqn.append(NoEscape(r'T_{db} &= min(T_{db1}, T_{db2})= ' + Tdb + r'\end{aligned}')) - # member_block_eqn.append(NoEscape(r'&= \frac{' + A_vg + '*' + f_y + '}{" 1.732*' + gamma_m0 + 'r'} + &+ +'\frac{"0.9*" + A_vn + '*' + f_u + '}{'+1.732+'*' + gamma_m0 + r'} '\\')) - - return member_block_eqn - - -def slenderness_req(): - - slenderlimit_eqn = Math(inline=True) - slenderlimit_eqn.append(NoEscape(r'\begin{aligned}\frac{K * L}{r} &\leq 400\end{aligned}')) - - return slenderlimit_eqn - - -def slenderness_prov(K, L, r, slender): - K = str(K) - L = str(L) - r = str(r) - slender = str(slender) - - slender_eqn = Math(inline=True) - slender_eqn.append(NoEscape(r'\begin{aligned}\frac{K * L}{r} &= \frac{'+K+'*'+L+'}{'+r+ r'}\\')) - slender_eqn.append(NoEscape(r'&= ' + slender + r'\end{aligned}')) - - return slender_eqn - - -def efficiency_req(): - efflimit_eqn = Math(inline=True) - efflimit_eqn.append(NoEscape(r'\begin{aligned} Utilization~Ratio &\leq 1 \end{aligned}')) - - return efflimit_eqn - - -def efficiency_prov(F, Td, eff): - F = str(F) - Td = str(round(Td/1000,2)) - eff = str(eff) - eff_eqn = Math(inline=True) - eff_eqn.append(NoEscape(r'\begin{aligned} Utilization~Ratio &= \frac{F}{Td}&=\frac{'+F+'}{'+Td+r'}\\')) - eff_eqn.append(NoEscape(r'&= ' + eff + r'\end{aligned}')) - - return eff_eqn - - -def gusset_ht_prov(beam_depth, clearance, height, mul = 1): - beam_depth = str(beam_depth) - clearance = str(clearance) - height = str(height) - mul = str(mul) - plate_ht_eqn = Math(inline=True) - plate_ht_eqn.append( - NoEscape(r'\begin{aligned} H &= '+mul+ '* Depth + clearance 'r'\\')) - plate_ht_eqn.append( - NoEscape(r'&=('+mul+'*' + beam_depth + ')+' + clearance + r'\\')) - plate_ht_eqn.append(NoEscape(r'&= ' + height + r'\end{aligned}')) - return plate_ht_eqn - - -def gusset_lt_b_prov(nc,p,e,length): - nc = str(nc) - p = str(p) - e = str(e) - length = str(length) - length_htb_eqn = Math(inline=True) - length_htb_eqn.append( - NoEscape(r'\begin{aligned} L &= (nc -1) * p + 2 * e\\')) - length_htb_eqn.append( - NoEscape(r'&= ('+nc+'-1) *'+ p + '+ (2 *'+ e + r')\\')) - length_htb_eqn.append(NoEscape(r'&= ' + length + r'\end{aligned}')) - return length_htb_eqn - - -def gusset_lt_w_prov(weld,cls,length): - weld = str(weld) - cls = str(cls) - length = str(length) - length_htw_eqn = Math(inline=True) - length_htw_eqn.append( - NoEscape(r'\begin{aligned} L &= Flange weld + clearance 'r'\\')) - length_htw_eqn.append( - NoEscape(r'&= '+ weld + '+' + cls + r'\\')) - length_htw_eqn.append(NoEscape(r'&= ' + length + r'\end{aligned}')) - return length_htw_eqn - - -def long_joint_bolted_req(): - long_joint_bolted_eqn = Math(inline=True) - long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} &if~l\geq 15 * d~then~V_{rd} = \beta_{ij} * V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'& if~l < 15 * d~then~V_{rd} = V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'& where,\\')) - long_joint_bolted_eqn.append(NoEscape(r'& l = ((nc~or~nr) - 1) * (p~or~g) \\')) - long_joint_bolted_eqn.append(NoEscape(r'& \beta_{ij} = 1.075 - l/(200 * d) \\')) - long_joint_bolted_eqn.append(NoEscape(r'& but~0.75\leq\beta_{ij}\leq1.0 \end{aligned}')) - - return long_joint_bolted_eqn - - -def long_joint_bolted_prov(nc,nr,p,g,d,Tc,Tr): - lc = (nc - 1) * p - lr = (nr - 1) * g - l = max(lc,lr) - lt = 15 * d - B = 1.075 - (l / (200 * d)) - Bi = round(B,2) - nc= str(nc) - nr= str(nr) - g= str(g) - p = str(p) - d = str(d) - Tc = str(Tc) - Tr = str(Tr) - if B<=0.75: - B =0.75 - elif B>=1: - B =1 - else: - B=B - B = str(round(B,2)) - Bi = str(Bi) - lc_str = str(lc) - lr_str = str(lr) - l_str = str(l) - lt_str = str(lt) - long_joint_bolted_eqn = Math(inline=True) - # long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} &if~l\leq 15 * d~then~V_{rd} = \beta_{ij} * V_{db} \\')) - # long_joint_bolted_eqn.append(NoEscape(r'& where,\\')) - if l < (lt): - long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} l&= ((nc~or~nr) - 1) * (p~or~g) \\')) - long_joint_bolted_eqn.append(NoEscape(r' &= ('+nc+' - 1) * '+p+ '='+lc_str+ r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' &= (' + nr + ' - 1) * ' + g + '=' + lr_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' l&= '+ l_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r'& 15 * d = 15 * '+d+' = '+lt_str +r' \\')) - long_joint_bolted_eqn.append(NoEscape(r'& since,~l < 15 * d~then~V_{rd} = V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'& V_{rd} = '+Tc+r' \end{aligned}')) - else: - long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} l&= ((nc~or~nr) - 1) * (p~or~g) \\')) - long_joint_bolted_eqn.append(NoEscape(r' &= (' + nc + ' - 1) * ' + p + '=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' &= (' + nr + ' - 1) * ' + g + '=' + lr_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' l&= ' + l_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r'& 15 * d = 15 * ' + d + ' = ' + lt_str + r' \\')) - long_joint_bolted_eqn.append(NoEscape(r'& since,~l \geq 15 * d~then~V_{rd} = \beta_{ij} * V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'& \beta_{ij} = 1.075 - '+ l_str +'/(200*'+d+') ='+Bi+r'\\')) - long_joint_bolted_eqn.append(NoEscape(r'& V_{rd} = '+B+' * '+Tc+'='+Tr+ r' \end{aligned}')) - - return long_joint_bolted_eqn - - -def long_joint_bolted_beam(nc,nr,p,g,d,Tc,Tr,joint,end_dist,gap,edge_dist,web_thickness,root_radius,conn=None): - - if joint == 'web': - lc = round(2*((nc/2 - 1) * p + end_dist) + gap ,2) - lr = round((nr - 1) * g,2) - else: - lc = round(2*((nc/2 - 1) * p + end_dist) +gap ,2) - lr = round(2*((nr/2 - 1) * g + edge_dist +root_radius ) + web_thickness ,2) - - l = round(max(lc,lr) ,2) - lt = 15 * d - B = 1.075 - (l / (200 * d)) - # Bi = round(B,2) - nc= str(nc) - nr= str(nr) - g= str(g) - p = str(p) - d = str(d) - Tc = str(Tc) - Tr = str(Tr) - if B<=0.75: - B =0.75 - elif B>=1: - B =1 - else: - B=B - B = round(B,2) - Bi = str(B) - lc_str = str(lc) - lr_str = str(lr) - l_str = str(l) - lt_str = str(lt) - end_dist =str(end_dist) - edge_dist = str(edge_dist) - web_thickness = str(web_thickness) - gap =str(gap) - root_radius =str(root_radius) - long_joint_bolted_eqn = Math(inline=True) - # long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} &if~l\leq 15 * d~then~V_{rd} = \beta_{ij} * V_{db} \\')) - # long_joint_bolted_eqn.append(NoEscape(r'& where,\\')) - if l < (lt): - if joint == 'web': - long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} l&= ((nc~or~nr) - 1) * (p~or~g) \\')) - if conn =="beam_beam": - long_joint_bolted_eqn.append(NoEscape( r' lr &= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lc &= (' + nr + ' - 1) * ' + g + '=' + lr_str + r'\\')) - elif conn =="col_col": - long_joint_bolted_eqn.append(NoEscape(r' lc &= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lr &= (' + nr + ' - 1) * ' + g + '=' + lr_str + r'\\')) - else: - long_joint_bolted_eqn.append(NoEscape(r' lc &= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lr &= (' + nr + ' - 1) * ' + g + '=' + lr_str + r'\\')) - - long_joint_bolted_eqn.append(NoEscape(r' l&= ' + l_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r'& 15 * d = 15 * '+d+' = '+lt_str +r' \\')) - long_joint_bolted_eqn.append(NoEscape(r'& since,~l < 15 * d~\\&then~V_{rd} = V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'& V_{rd} = '+Tc+r' \end{aligned}')) - else: - long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} l~&= ((nc~or~nr) - 1) * (p~or~g) \\')) - if conn == "beam_beam": - long_joint_bolted_eqn.append(NoEscape(r' lr&= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lc&= 2*((\frac{' + nr + '}{2} - 1) * ' + g + '+' + edge_dist + r'\\& +' + root_radius + ')+ ' + web_thickness + '=' + lr_str + r'\\')) - elif conn == "col_col": - long_joint_bolted_eqn.append(NoEscape(r' lc&= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lr&= 2*((\frac{' + nr + '}{2} - 1) * ' + g + '+' + edge_dist + r'\\& +' + root_radius + ')+ ' + web_thickness + '=' + lr_str + r'\\')) - else: - long_joint_bolted_eqn.append(NoEscape( r' lc&= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lr&= 2*((\frac{' + nr + '}{2} - 1) * ' + g + '+' + edge_dist + r'\\& +' + root_radius + ')+ ' + web_thickness + '=' + lr_str + r'\\')) - - long_joint_bolted_eqn.append(NoEscape(r' l~&= ' + l_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r'& 15 * d = 15 * ' + d + ' = ' + lt_str + r' \\')) - long_joint_bolted_eqn.append(NoEscape(r'& since,~l < 15 * d~ \\& then~V_{rd} = V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'& V_{rd} = ' + Tc + r' \end{aligned}')) - else: - if joint == 'web': - long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} l&= ((nc~or~nr) - 1) * (p~or~g) \\')) - if conn == "beam_beam": - long_joint_bolted_eqn.append(NoEscape(r' lr&= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lc&= (' + nr + ' - 1) * ' + g + '=' + lr_str + r'\\')) - elif conn == "col_col": - long_joint_bolted_eqn.append(NoEscape(r' lc&= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lr&= (' + nr + ' - 1) * ' + g + '=' + lr_str + r'\\')) - else: - long_joint_bolted_eqn.append(NoEscape(r' lc&= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lr&= (' + nr + ' - 1) * ' + g + '=' + lr_str + r'\\')) - - long_joint_bolted_eqn.append(NoEscape(r' l&= ' + l_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r'& 15 * d = 15 * ' + d + ' = ' + lt_str + r' \\')) - long_joint_bolted_eqn.append(NoEscape(r'&since,~l \geq 15 * d~ \\&then~V_{rd} = \beta_{ij} * V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'\beta_{ij} &= 1.075 - '+ l_str +'/(200*'+d+r') \\&='+Bi+r'\\')) - long_joint_bolted_eqn.append(NoEscape(r'V_{rd} &= '+Bi+' * '+Tc+'='+Tr+ r' \end{aligned}')) - else: - long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} l~&= ((nc~or~nr) - 1) * (p~or~g) \\')) - if conn == "beam_beam": - long_joint_bolted_eqn.append(NoEscape(r' lr&= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lc&= 2*((\frac{' + nr + '}{2} - 1) * ' + g + '+' + edge_dist +r'\\& +'+root_radius+')+ ' + web_thickness + '=' + lr_str + r'\\')) - elif conn == "col_col": - long_joint_bolted_eqn.append(NoEscape(r' lc&= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' lr&= 2*((\frac{' + nr + '}{2} - 1) * ' + g + '+' + edge_dist + r'\\& +' + root_radius + ')+ ' + web_thickness + '=' + lr_str + r'\\')) - else: - long_joint_bolted_eqn.append(NoEscape(r' lc&= 2*((\frac{' + nc + '}{2} - 1) * ' + p + '+' + end_dist + ')+ ' + gap + r'\\&=' + lc_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape( r' lr&= 2*((\frac{' + nr + '}{2} - 1) * ' + g + '+' + edge_dist + r'\\& +' + root_radius + ')+ ' + web_thickness + '=' + lr_str + r'\\')) - - long_joint_bolted_eqn.append(NoEscape(r' l~&= ' + l_str + r'\\')) - long_joint_bolted_eqn.append(NoEscape(r'&15 * d = 15 * ' + d + ' = ' + lt_str + r' \\')) - long_joint_bolted_eqn.append(NoEscape(r'&since,~l \geq 15 * d~\\ &then~V_{rd} = \beta_{ij} * V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'\beta_{ij} &= 1.075 - '+ l_str +'/(200*'+d+ r')\\& ='+Bi+r'\\')) - long_joint_bolted_eqn.append(NoEscape(r' V_{rd}& = '+Bi+' * '+Tc+'='+Tr+ r' \end{aligned}')) - - return long_joint_bolted_eqn - - -def long_joint_welded_req(): - long_joint_bolted_eqn = Math(inline=True) - long_joint_bolted_eqn.append(NoEscape(r'\begin{aligned} &if~l\geq 150 * t_t~then~V_{rd} = \beta_{l_w} * V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'& if~l < 150 * t_t~then~V_{rd} = V_{db} \\')) - long_joint_bolted_eqn.append(NoEscape(r'& where,\\')) - long_joint_bolted_eqn.append(NoEscape(r'& l ~= pt.length ~ or ~ pt.height \\')) - long_joint_bolted_eqn.append(NoEscape(r'& \beta_{l_w} = 1.2 - \frac{(0.2*l )}{(150*t_t)} \\')) - long_joint_bolted_eqn.append(NoEscape(r'& but~0.6\leq\beta_{l_w}\leq1.0 \end{aligned}')) - - - return long_joint_bolted_eqn - -def long_joint_welded_beam_prov(plate_height,l_w,t_w,gap,t_t,Tc,Tr): - ll = round(2*(l_w +(2*t_w)) +gap,2) - lh = plate_height - l = round(max(ll,lh) ,2) - lt = 150 * t_t - B = 1.2 - ((0.2 * l) / (150 * t_t)) - if B <= 0.6: - B = 0.6 - elif B >= 1: - B = 1 - else: - B = B - Bi = str(round(B, 2)) - t_t= str(t_t) - # l =str(l) - l_str= str(l) - # lt = str(lt) - lt_str = str(lt) - - # B = str(round(B, 2)) - # Bi = str(Bi) - t_t= str(t_t) - ll_str =str(ll) - lh_str= str(lh) - plate_height =str(plate_height) - Tc = str(Tc) - Tr = str(Tr) - l_w = str(l_w ) - - t_w = str(t_w) - l_w = str(l_w) - gap = str(gap) - long_joint_welded_beam_prov = Math(inline=True) - # if conn =="web": - if l < lt: - long_joint_welded_beam_prov.append(NoEscape(r'\begin{aligned} l ~&= pt.length ~ or ~ pt.height \\')) - long_joint_welded_beam_prov.append(NoEscape(r' l_l &= 2('+l_w +'+(2*'+t_w+'))+'+gap+r' \\')) - long_joint_welded_beam_prov.append(NoEscape(r' &='+ ll_str+ r' \\')) - long_joint_welded_beam_prov.append(NoEscape(r'l_h& =' +lh_str+r' \\')) - long_joint_welded_beam_prov.append(NoEscape(r' l~&= ' + l_str + r'\\')) - long_joint_welded_beam_prov.append(NoEscape(r'& 150 * t_t =150 * '+t_t+' = '+lt_str +r' \\')) - long_joint_welded_beam_prov.append(NoEscape(r'& since,~l < 150 * t_t~\\&then~V_{rd} = V_{db} \\')) - long_joint_welded_beam_prov.append(NoEscape(r' V_{rd} &= ' + Tc + r' \end{aligned}')) - else: - long_joint_welded_beam_prov.append(NoEscape(r'\begin{aligned} l~&= pt.length ~or ~pt.height \\')) - long_joint_welded_beam_prov.append(NoEscape(r' l_l &= 2(' + l_w + '+(2*' + t_w + '))+' + gap + r' \\')) - long_joint_welded_beam_prov.append(NoEscape(r' &=' + ll_str + r' \\')) - long_joint_welded_beam_prov.append(NoEscape(r' l_h& =' + lh_str + r' \\')) - long_joint_welded_beam_prov.append(NoEscape(r' l~&= ' + l_str + r'\\')) - long_joint_welded_beam_prov.append(NoEscape(r'& 150 * t_t =150 * ' + t_t + ' = ' + lt_str + r' \\')) - long_joint_welded_beam_prov.append(NoEscape(r'&since,~l \geq 150 * t_t~ \\&then~V_{rd} = \beta_{lw} * V_{db} \\')) - long_joint_welded_beam_prov.append(NoEscape(r'\beta_{l_w}& = 1.2 - (0.2*' + l_str + ')/(150*' + t_t+ r')\\& =' + Bi + r'\\')) - long_joint_welded_beam_prov.append(NoEscape(r' V_{rd}& = ' + Bi + ' * ' + Tc + '=' + Tr + r' \end{aligned}')) - - return long_joint_welded_beam_prov - -def long_joint_welded_prov(h,l,t_t,ws,wsr): - lj = max(h,l) - lt = 150 * t_t - B = 1.2 - ((0.2 * lj) / (150 * t_t)) - if B <= 0.6: - B = 0.6 - elif B >= 1: - B = 1 - else: - B = B - Bi = str(round(B, 2)) - t_t= str(t_t) - lt_str = str(lt) - h = str(h) - l = str(l) - ws = str(ws) - wsr = str(wsr) - ljs= str(lj) - - long_joint_welded_prov = Math(inline=True) - # if conn =="web": - if lj < lt: - long_joint_welded_prov.append(NoEscape(r'\begin{aligned} l ~&= pt.length ~ or ~ pt.height \\')) - long_joint_welded_prov.append(NoEscape(r' l_l &= max('+h +','+l+r') \\')) - long_joint_welded_prov.append(NoEscape(r' &='+ ljs + r' \\')) - long_joint_welded_prov.append(NoEscape(r'& 150 * t_t =150 * '+t_t+' = '+lt_str +r' \\')) - long_joint_welded_prov.append(NoEscape(r'& since,~l < 150 * t_t~\\&then~V_{rd} = V_{db} \\')) - long_joint_welded_prov.append(NoEscape(r' V_{rd} &= ' + ws + r' \end{aligned}')) - else: - long_joint_welded_prov.append(NoEscape(r'\begin{aligned} l~&= pt.length ~or ~pt.height \\')) - long_joint_welded_prov.append(NoEscape(r' l_l &= max(' + h + ',' + l + r') \\')) - long_joint_welded_prov.append(NoEscape(r' &=' + ljs + r' \\')) - long_joint_welded_prov.append(NoEscape(r'& 150 * t_t =150 * ' + t_t + ' = ' + lt_str + r' \\')) - long_joint_welded_prov.append(NoEscape(r'&since,~l \geq 150 * t_t~ \\&then~V_{rd} = \beta_{lw} * V_{db} \\')) - long_joint_welded_prov.append(NoEscape(r'\beta_{l_w}& = 1.2 - (0.2*' + lj + ')/(150*' + t_t+ r')\\& =' + Bi + r'\\')) - long_joint_welded_prov.append(NoEscape(r' V_{rd}& = ' + Bi + ' * ' + ws + '=' + wsr + r' \end{aligned}')) - - return long_joint_welded_prov - - -def throat_req(): - throat_eqn = Math(inline=True) - throat_eqn.append(NoEscape(r'\begin{aligned} t_t &\geq 3 \end{aligned}')) - - return throat_eqn - - -def throat_prov(tw,f): - tt = tw * f - t_t= max(tt,3) - tw = str(round(tw,2)) - f= str(round(f,2)) - tt = str(round(tt,2)) - t_t = str(round(t_t,2)) - - throat_eqn = Math(inline=True) - throat_eqn.append(NoEscape(r'\begin{aligned} t_t & = '+ f+'* t_w 'r'\\')) - throat_eqn.append(NoEscape(r'& = ' + f + '*'+ tw +r'\\')) - throat_eqn.append(NoEscape(r't_t & = ' + t_t + r'\end{aligned}')) - - return throat_eqn - - -def eff_len_prov(l_eff, b_fp, t_w, l_w,con= None): - l_eff = str(l_eff) - l_w = str(l_w) - b_fp = str(b_fp) - t_w = str(t_w) - eff_len_eqn = Math(inline=True) - if con == "Flange": - eff_len_eqn.append(NoEscape(r'\begin{aligned} l_{eff} &= (2*l_w) + B_{fp} - 2*t_w\\')) - eff_len_eqn.append(NoEscape(r'&= (2*' + l_w + ') +' + b_fp + ' - 2*' + t_w + r'\\')) - eff_len_eqn.append(NoEscape(r'& = ' + l_eff + r'\end{aligned}')) - else: - eff_len_eqn.append(NoEscape(r'\begin{aligned} l_{eff} &= (2*l_w) + W_{wp} - 2*t_w\\')) - eff_len_eqn.append(NoEscape(r'&= (2*' + l_w + ') +' + b_fp + ' - 2*' + t_w + r'\\')) - eff_len_eqn.append(NoEscape(r'& = ' + l_eff + r'\end{aligned}')) - - return eff_len_eqn - - -def eff_len_prov_out_in(l_eff, b_fp,b_ifp, t_w, l_w): - l_eff = str(l_eff) - l_w = str(l_w) - b_fp = str(b_fp) - b_ifp = str(b_ifp) - t_w = str(t_w) - eff_len_prov_out_in_eqn = Math(inline=True) - eff_len_prov_out_in_eqn.append(NoEscape(r'\begin{aligned} l_{eff} &= (6*l_w) + B_{fp} + (2 * B_{ifp})- 6*t_w\\')) - eff_len_prov_out_in_eqn.append(NoEscape(r'&= (6*' + l_w + ') +' + b_fp + '+ 2*' +b_ifp + '- 6*' + t_w + r'\\')) - eff_len_prov_out_in_eqn.append(NoEscape(r'& = ' + l_eff + r'\end{aligned}')) - - return eff_len_prov_out_in_eqn - - -def eff_len_req(F_f, l_eff_req, F_wd): - F_f = str(F_f) - l_eff_req = str(l_eff_req) - F_wd = str(F_wd) - flange_weld_stress_eqn = Math(inline=True) - flange_weld_stress_eqn.append(NoEscape(r'\begin{aligned} l_{eff}_{req} &= \frac{F_f*10^3}{F_{wd}}\\')) - flange_weld_stress_eqn.append(NoEscape(r' &= \frac{' + F_f + '*10^3}{' + F_wd + r'}\\')) - flange_weld_stress_eqn.append(NoEscape(r'&= ' + l_eff_req + r'\end{aligned}')) - - return flange_weld_stress_eqn - - -def plate_area_req(crs_area, flange_web_area): - crs_area = str(crs_area) - flange_web_area = str(flange_web_area) - - plate_crs_sec_area_eqn =Math(inline=True) - plate_crs_sec_area_eqn.append(NoEscape(r'\begin{aligned} &pt.area >= \\&connected~member~area * 1.05\\')) - # plate_crs_sec_area_eqn.append(NoEscape(r'& = '+crs_area+ r' * 1.05 \\')) - plate_crs_sec_area_eqn.append(NoEscape(r' &= ' + flange_web_area + r'\end{aligned}')) - return plate_crs_sec_area_eqn - - -def width_pt_chk(B,t,r_1,pref): - if pref == "Outside": - outerwidth = round(B - (2 * 21) ,2) - outerwidth = str(outerwidth) - else: - innerwidth = round((B -t - (2*r_1)-(4*21))/2 ,2) - innerwidth = str(innerwidth) - - B = str(B) - t = str(t) - r_1 = str(r_1) - Innerwidth_pt_chk_eqn = Math(inline=True) - if pref == "Outside": - Innerwidth_pt_chk_eqn.append(NoEscape(r'\begin{aligned} B_{fp} &= B-(2*21)\\')) - Innerwidth_pt_chk_eqn.append(NoEscape(r'&=' + B + r'-(2*21)\\')) - Innerwidth_pt_chk_eqn.append(NoEscape(r'&= ' + outerwidth +r'\end{aligned}')) - else: - Innerwidth_pt_chk_eqn.append(NoEscape(r'\begin{aligned} B_{ifp} &= \frac{B-t-(2*R1)-(4 * 21)}{2}\\')) - Innerwidth_pt_chk_eqn.append(NoEscape(r'&=\frac{'+B+'-'+t+'-(2*'+r_1+r')-(4 * 21)}{2}\\')) - Innerwidth_pt_chk_eqn.append(NoEscape(r'&= ' + innerwidth + r'\end{aligned}')) - return Innerwidth_pt_chk_eqn - - -def width_pt_chk_bolted(B,t,r_1): - innerwidth =round((B -t - (2*r_1))/2 ,2) - B = str(B) - t = str(t) - r_1 = str(r_1) - innerwidth = str(innerwidth) - width_pt_chk_bolted_eqn = Math(inline=True) - width_pt_chk_bolted_eqn.append(NoEscape(r'\begin{aligned} B_{fp} &= \frac{B-t-(2*R1)}{2}\\')) - width_pt_chk_bolted_eqn.append(NoEscape(r'&=\frac{' + B + '-' + t + '-(2*' + r_1 + r')}{2}\\')) - width_pt_chk_bolted_eqn.append(NoEscape(r'&= ' + innerwidth + r'\end{aligned}')) - return width_pt_chk_bolted_eqn - - -def web_width_chk_bolt (pref,D,tk,T,R_1,webplatewidth,webclearance = None): - T = str(T) - tk = str(tk) - R_1 = str(R_1) - webplatewidth= str(webplatewidth) - webclearance =str(webclearance) - web_width_chk_bolt_eqn = Math(inline=True) - if pref =="Outside": - D = str(D) - web_width_chk_bolt_eqn.append(NoEscape(r'\begin{aligned} W_{wp} &= D - (2 * T) - (2 * R1)\\')) - web_width_chk_bolt_eqn.append(NoEscape(r' &= '+D+' - (2 * '+T+') - (2 *'+ R_1+r')\\')) - web_width_chk_bolt_eqn.append(NoEscape(r' &=' + webplatewidth + r'\end{aligned}')) - else : - - if D > 600.00: - web_width_chk_bolt_eqn.append(NoEscape(r'\begin{aligned} C~~ &= max((R1, t_{ifp}) + 25) \\')) - web_width_chk_bolt_eqn.append(NoEscape(r'&= max(('+R_1+',' +tk+r') + 25) \\')) - web_width_chk_bolt_eqn.append(NoEscape(r'&= ' + webclearance + r' \\')) - - else: - web_width_chk_bolt_eqn.append(NoEscape(r'\begin{aligned} C~~ &= max((R1, t_{ifp}) + 10) \\')) - web_width_chk_bolt_eqn.append(NoEscape(r'&= max((' + R_1 + ',' +tk + r') +10) \\')) - web_width_chk_bolt_eqn.append(NoEscape(r'&= ' + webclearance + r' \\')) - D = str(D) - web_width_chk_bolt_eqn.append(NoEscape(r' W_{wp} &= D - (2 * T) - (2 * C)\\')) - web_width_chk_bolt_eqn.append(NoEscape(r' &= ' + D + ' - (2 * ' + T + ') - (2 *' + webclearance + r')\\')) - web_width_chk_bolt_eqn.append(NoEscape(r' &='+webplatewidth + r'\end{aligned}')) - - return web_width_chk_bolt_eqn - - -def web_width_chk_weld (D,tk,R_1,webplatewidth): - tk = str(tk) - R_1 = str(R_1) - D = str(D) - webplatewidth = str(webplatewidth) - web_width_chk_weld_eqn = Math(inline=True) - web_width_chk_weld_eqn.append(NoEscape(r'\begin{aligned} W_{wp} &= D - (2 * T) - (2 * R1)- (2*21)\\')) - web_width_chk_weld_eqn.append(NoEscape(r' &= ' + D + ' - (2 * ' + tk + ') - (2 *' + R_1 + r')- (2*21)\\')) - web_width_chk_weld_eqn.append(NoEscape(r' &=' + webplatewidth + r'\end{aligned}')) - return web_width_chk_weld_eqn - - -def web_width_min (D,min_req_width): - D = str(D) - min_req_width = str(min_req_width) - web_width_min_eqn = Math(inline=True) - web_width_min_eqn.append(NoEscape(r'\begin{aligned} &= 0.6 *D\\')) - web_width_min_eqn.append(NoEscape(r' &= 0.6 *'+D+r'\\')) - web_width_min_eqn.append(NoEscape(r' &= ' + min_req_width+ r'\end{aligned}')) - return web_width_min_eqn - - -def flange_plate_area_prov(B,pref,y,outerwidth,fp_area,t,r_1,innerwidth =None,): - outerwidth = str(outerwidth) - B = str(B) - fp_area = str(fp_area) - t = str(t) - r_1 = str(r_1) - innerwidth = str(innerwidth) - y = str(y) - flangeplate_crs_sec_area_eqn = Math(inline=True) - - if pref == "Outside": - flangeplate_crs_sec_area_eqn.append(NoEscape(r'\begin{aligned} B_{fp} &= B - (2 * 21)\\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r'&= '+B+r' - (2 * 21)\\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r'&= ' + outerwidth + r' \\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r' pt.area &= '+y+' * '+outerwidth+r'\\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r'&= '+fp_area+r'\end{aligned}')) - else: - flangeplate_crs_sec_area_eqn.append(NoEscape(r'\begin{aligned} B_{fp} &= B-(2*21)\\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r'&='+B+ r'-(2*21)\\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r'&= ' + outerwidth + r' \\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r'B_{ifp}&= \frac{B-t-(2*R1)-(4 * 21)}{2}\\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r'&=\frac{'+B+'-'+t+'-(2*'+r_1+r')-(4 * 21)}{2}\\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r'&= ' + innerwidth + r' \\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r' pt.area &=('+outerwidth+'+(2*'+innerwidth+'))*'+y+r'\\')) - flangeplate_crs_sec_area_eqn.append(NoEscape(r'&= ' + fp_area + r'\end{aligned}')) - - - return flangeplate_crs_sec_area_eqn - - -def plate_recheck_area_weld(outerwidth,innerwidth=None,f_tp=None,t_wp=None,conn=None,pref=None): - - if conn == "flange": - if pref =="Outside": - flange_plate_area = (outerwidth * f_tp) - - else: - flange_plate_area = (outerwidth + (2 * innerwidth)) * f_tp - # flange_plate_area = str(flange_plate_area) - else : - web_plate_area = (2 * outerwidth * t_wp) - # web_plate_area = str(web_plate_area) - outerwidth = str(outerwidth) - innerwidth = str(innerwidth) - f_tp= str(f_tp) - t_wp = str(t_wp) - # flange_plate_area = str(flange_plate_area) - # web_plate_area = str(web_plate_area) - plate_recheck_area_weld_eqn = Math(inline=True) - if conn == "flange": - if pref =="Outside": - flange_plate_area = str(flange_plate_area) - plate_recheck_area_weld_eqn.append(NoEscape(r'\begin{aligned} pt.area &=B_{fp} * t_{ifp}\\')) - plate_recheck_area_weld_eqn.append(NoEscape(r' &='+outerwidth+'*'+f_tp+r'\\')) - plate_recheck_area_weld_eqn.append(NoEscape(r'&= ' + flange_plate_area + r'\end{aligned}')) - else: - flange_plate_area = str(flange_plate_area) - plate_recheck_area_weld_eqn.append(NoEscape(r'\begin{aligned} pt.area &=(B_{fp} +(2* B_{ifp}))* t_{ifp} \\')) - plate_recheck_area_weld_eqn.append(NoEscape(r' &=(' + outerwidth + '+(2*' + innerwidth + '))*' + f_tp + r'\\')) - plate_recheck_area_weld_eqn.append(NoEscape(r'&= ' + flange_plate_area +r'\end{aligned}')) - else: - web_plate_area = str(web_plate_area) - plate_recheck_area_weld_eqn.append(NoEscape(r'\begin{aligned} pt.area &=2*W_{wp} * t_{wp} \\')) - plate_recheck_area_weld_eqn.append(NoEscape(r' &=2*' + outerwidth +' *' + t_wp + r'\\')) - plate_recheck_area_weld_eqn.append(NoEscape(r'&= ' + web_plate_area + r'\end{aligned}')) - return plate_recheck_area_weld_eqn - -def flange_plate_area_prov_bolt(B,pref,y,outerwidth,fp_area,t,r_1,innerwidth =None): - outerwidth = str(outerwidth) - B = str(B) - fp_area = str(fp_area) - t = str(t) - r_1 = str(r_1) - innerwidth = str(innerwidth) - y = str(y) - flangeplate_crs_sec_area_bolt_eqn = Math(inline=True) - if pref == "Outside": - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r'\begin{aligned} B_{fp} &= B\\')) - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r'&= ' + outerwidth +r'\\')) - - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r' pt.area &= ' + y + ' * ' + outerwidth + r'\\')) - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r'&= ' + fp_area + r'\end{aligned}')) - else: - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r'\begin{aligned} B_{fp} &= B\\')) - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r'&= ' + outerwidth + r' \\')) - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r'B_{ifp} &= \frac{B-t-(2*R1)}{2}\\')) - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r'&=\frac{' + B + '-' + t + '-(2*' + r_1 + r')}{2}\\')) - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r'&= ' + innerwidth + r' \\')) - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r' pt.area &=(' + outerwidth + '+(2*' + innerwidth + '))*' + y + r'\\')) - flangeplate_crs_sec_area_bolt_eqn.append(NoEscape(r'&= ' + fp_area + r'\end{aligned}')) - - return flangeplate_crs_sec_area_bolt_eqn - - -def web_plate_area_prov(D, y, webwidth, wp_area, T, r_1): - D = str(D) - T = str(T) - r_1 = str(r_1) - webwidth = str(webwidth) - wp_area =str(wp_area) - y =str(y) - - web_plate_area_prov = Math(inline=True) - # web_plate_area_prov.append(NoEscape(r'\begin{aligned} W_{wp}&= D-(2*T)-(2*R1)-(2*21)\\')) - # web_plate_area_prov.append(NoEscape(r'&='+D+'-(2*'+T+')-(2*'+r_1+r')-(2*21)\\')) - # web_plate_area_prov.append(NoEscape(r'&= ' + webwidth + r' \\')) - web_plate_area_prov.append(NoEscape(r'\begin{aligned} pt.area &= t_{wp}*2* W_{wp}\\')) - web_plate_area_prov.append(NoEscape(r' &= ' + y + '*2* ' + webwidth + r'\\')) - web_plate_area_prov.append(NoEscape(r'&= ' + wp_area + r'\end{aligned}')) - return web_plate_area_prov - - -def tension_in_bolt_due_to_axial_load_n_moment(P,n,M,y_max,y_sqr,T_b): - P= str(P) - n = str(n) - M = str(M) - y_max =str(y_max) - y_sqr = str(y_sqr) - T_b = str (T_b) - tension_in_bolt_due_to_axial_load_n_moment = Math(inline=True) - tension_in_bolt_due_to_axial_load_n_moment.append(NoEscape(r'\begin{aligned} T_b &= \frac{P}{\ n} + \frac{M * y_{max}}{\ y_{sqr}}\\')) - tension_in_bolt_due_to_axial_load_n_moment.append(NoEscape(r'&=\frac{' +P + '}{' + n + r'} + \frac{' +M + '*' + y_max+ r'}{' + y_sqr + r'}\\')) - tension_in_bolt_due_to_axial_load_n_moment.append(NoEscape(r'&= ' + T_b + r'\end{aligned}')) - return tension_in_bolt_due_to_axial_load_n_moment - -def moment_cap(beta,m_d,f_y,gamma_m0,m_fd,mom_cap): - beta= str(beta) - m_d= str(m_d) - f_y= str(f_y) - gamma_m0 = str(gamma_m0) - m_fd = str(m_fd) - mom_cap = str(mom_cap) - moment_cap =Math(inline=True) - - moment_cap.append(NoEscape(r'\begin{aligned} M_{c} &= m_d - \beta(m_d -m_{fd}) \\')) - moment_cap.append(NoEscape(r'&= ' + m_d + r'-' + beta + r'('+m_d+r'-'+m_fd+r') \\')) - moment_cap.append(NoEscape(r'&= ' + mom_cap + r'\end{aligned}')) - return moment_cap - -def moment_CAP( m_d, f_y, gamma_m0, Z_e, mom_cap): - m_d = str(m_d) - f_y = str(f_y) - gamma_m0 = str(gamma_m0) - Z_e = str(Z_e) - mom_cap = str(mom_cap) - moment_cap = Math(inline=True) - - moment_cap.append(NoEscape(r'\begin{aligned} mom_cap &= \frac{Z_e*f_y}{\ gamma_m0} \\')) - moment_cap.append(NoEscape(r'&= \frac{' + Z_e + '*'+f_y+ r'}{' +gamma_m0+r'} \\')) - moment_cap.append(NoEscape(r'&= ' + mom_cap + r'\end{aligned}')) - return moment_CAP - -def no_of_bolts_along_web(D,T_f,e,p,n_bw): - D = str(D) - e= str(e) - p = str(p) - T_f = str(T_f) - n_bw = str(n_bw) - no_of_bolts_along_web = Math(inline=True) - no_of_bolts_along_web.append(NoEscape(r'\begin{aligned} n_{bw} &= \frac{D -(2*T_f) -(2*e)}{\ p} + 1 \\')) - no_of_bolts_along_web.append(NoEscape(r'&= \frac{' + D + ' -(2*'+T_f +')-(2*'+e + r')}{' + p + r'} +1 \\')) - no_of_bolts_along_web.append(NoEscape(r'&= ' + n_bw + r'\end{aligned}')) - return no_of_bolts_along_web - -def no_of_bolts_along_flange(b,T_w,e,p,n_bf): - b = str(b) - e= str(e) - p = str(p) - T_w = str(T_w) - n_bf = str(n_bf) - no_of_bolts_along_flange = Math(inline=True) - no_of_bolts_along_flange.append(NoEscape(r'\begin{aligned} n_{bf} &= \frac{b/2 -(T_w / 2) -(2*e)}{\ p} + 1 \\')) - no_of_bolts_along_flange.append(NoEscape(r'&= \frac{0.5*' + b + ' -(0.5*'+T_w +')-(2*'+e + r')}{' + p + r'} +1 \\')) - no_of_bolts_along_flange.append(NoEscape(r'&= ' + n_bf + r'\end{aligned}')) - return no_of_bolts_along_flange - - -def shear_force_in_bolts_near_web(V,n_wb,V_sb): - V = str(V) - n_wb = str(n_wb) - V_sb = str(V_sb) - shear_force_in_bolts_near_web = Math(inline=True) - shear_force_in_bolts_near_web.append(NoEscape(r'\begin{aligned} V_{sb} &= \frac{V}{\ n_{wb}} \\')) - shear_force_in_bolts_near_web.append(NoEscape(r'&=\frac{' + V + '}{' + n_wb + r'} \\')) - shear_force_in_bolts_near_web.append(NoEscape(r'&= ' + V_sb + r'\end{aligned}')) - return shear_force_in_bolts_near_web - -def tension_capacity_of_bolt(f_ub,A_nb,T_db): - f_ub= str(f_ub) - A_nb= str(A_nb) - T_db= str(T_db) - tension_capacity_of_bolt = Math(inline=True) - tension_capacity_of_bolt.append(NoEscape(r'\begin{aligned} T_{db} &= 0.9*A_{nb}*f_{ub}\\')) - tension_capacity_of_bolt.append(NoEscape(r'&=0.9*'+A_nb+ r'*'+f_ub+ r'\\')) - tension_capacity_of_bolt.append(NoEscape(r'&= ' + T_db+ r'\end{aligned}')) - return tension_capacity_of_bolt - - - -def web_plate_area_prov_bolt(D, y, webwidth, wp_area, T, r_1): - D = str(D) - T = str(T) - r_1 = str(r_1) - webwidth = str(webwidth) - wp_area = str(wp_area) - y = str(y) - - web_plate_area_prov = Math(inline=True) - # web_plate_area_prov.append(NoEscape( W_{wp}&= D-(2*T)-(2*R1)\\')) - # web_plate_area_prov.append(NoEscape(r'&=' + D + '-(2*' + T + ')-(2*' + r_1 + r')\\')) - # web_plate_area_prov.append(NoEscape(r'&= ' + webwidth + r' \\')) - web_plate_area_prov.append(NoEscape(r'\begin{aligned}pt.area &= t_{wp} *2* W_{wp} \\')) - web_plate_area_prov.append(NoEscape(r'&= ' + y + '*2* ' + webwidth + r'\\')) - web_plate_area_prov.append(NoEscape(r'&= ' + wp_area + r'\end{aligned}')) - return web_plate_area_prov - - -# def eff_len_prov(l): -# l =str(l) -# eff_len_eqn = Math(inline=True) -# eff_len_eqn.append(NoEscape(r'\begin{aligned} l_w &='+l+ r' \end{aligned}')) -# -# return eff_len_eqn -# -# def diameter_prov(d): -# d = str(d) -# diameter_eqn = Math(inline=True) -# diameter_eqn.append(NoEscape(r'\begin{aligned} d &=' + d + r' \end{aligned}')) -# -# return diameter_eqn -# -# def diahole_prov(d0): -# d0 = str(d0) -# diahole_eqn = Math(inline=True) -# diahole_eqn.append(NoEscape(r'\begin{aligned} d &=' + d0 + r' \end{aligned}')) - - # return diahole_eqn - - -def display_prov(v,t, ref = None): - - v = str(v) - display_eqn = Math(inline=True) - if ref is not None: - display_eqn.append(NoEscape(r'\begin{aligned} '+t+' &=' + v + '~('+ref+r')\end{aligned}')) - else: - display_eqn.append(NoEscape(r'\begin{aligned} ' + t + ' &=' + v + r'\end{aligned}')) - return display_eqn - - -def gamma(v,t): - v = str(v) - gamma = Math(inline=True) - gamma.append(NoEscape(r'\begin{aligned}\gamma_{' + t + '}&=' + v + r'\end{aligned}')) - - return gamma - - -def kb_prov(e,p,d,fub,fu): - kb1 = round((e / (3.0 * d)),2) - kb2 = round((p / (3.0 * d)-0.25),2) - kb3 = round(( fub / fu),2) - kb4 = 1.0 - kb_1 = min(kb1,kb2,kb3,kb4) - kb_2 = min(kb1,kb3,kb4) - pitch = p - e = str(e) - p = str(p) - d = str(d) - fub = str(fub) - fu = str(fu) - kb1 = str(kb1) - kb2 = str(kb2) - kb3 = str(kb3) - kb4 = str(kb4) - kb_1 = str(kb_1) - kb_2 = str(kb_2) - kb_eqn = Math(inline=True) - if pitch != 0 : - kb_eqn.append(NoEscape(r'\begin{aligned} k_b & = min(\frac{e}{3*d_0},\frac{p}{3*d_0}-0.25,\frac{f_{ub}}{f_u},1.0)\\' )) - kb_eqn.append(NoEscape(r'& = min(\frac{'+e+'}{3*'+d+r'},\frac{'+p+'}{3*'+d+r'}-0.25,\frac{'+fub+'}{'+fu+r'},1.0)\\')) - kb_eqn.append(NoEscape(r'& = min('+kb1+','+kb2+','+kb3+','+kb4+r')\\')) - kb_eqn.append(NoEscape(r'& = '+kb_1+r'\end{aligned}')) - - else: - kb_eqn.append(NoEscape(r'\begin{aligned} k_b & = min(\frac{e}{3*d_0},\frac{f_{ub}}{f_u},1.0)\\')) - kb_eqn.append(NoEscape(r'& = min(\frac{' + e + '}{3*' + d + r'},\frac{' + fub + '}{' + fu + r'},1.0)\\')) - kb_eqn.append(NoEscape(r'& = min(' + kb1 + ',' + kb3 + ',' + kb4 + r')\\')) - kb_eqn.append(NoEscape(r'& = ' + kb_2 + r'\end{aligned}')) - return kb_eqn - - -def depth_req(e, g, row, sec =None): - d = 2*e + (row-1)*g - depth = d - depth = str(depth) - e = str(e) - g = str(g) - row = str(row) - - depth_eqn = Math(inline=True) - if sec == "C": - depth_eqn.append(NoEscape(r'\begin{aligned} depth & = 2 * e + (rl -1) * g \\')) - depth_eqn.append(NoEscape(r'& = 2 * '+e+'+('+row+'-1)*'+g+r' \\')) - depth_eqn.append(NoEscape(r'& = ' + depth + r'\end{aligned}')) - elif sec == "column": - depth_eqn.append(NoEscape(r'\begin{aligned} depth & = 2 * e + (c_l -1) * g\\')) - depth_eqn.append(NoEscape(r'& = 2 * ' + e + '+(' + row + '-1)*' + g + r'\\')) - depth_eqn.append(NoEscape(r'& = ' + depth + r'\end{aligned}')) - elif sec =="beam": - depth_eqn.append(NoEscape(r'\begin{aligned} depth & = 2 * e + (r_l -1) * g\\')) - depth_eqn.append(NoEscape(r'& = 2 * ' + e + '+(' + row + '-1)*' + g + r'\\')) - depth_eqn.append(NoEscape(r'& = ' + depth + r'\end{aligned}')) - else: - depth_eqn.append(NoEscape(r'\begin{aligned} depth & = 2 * e + (r_l -1) * g\\')) - depth_eqn.append(NoEscape(r'& = 2 * ' + e + '+(' + row + '-1)*' + g + r'\\')) - depth_eqn.append(NoEscape(r'& = ' + depth + r'\end{aligned}')) - - return depth_eqn - - # slender = (float(K) * float(L)) / float(r) - # - # self.slenderness = round(slender, 2) - # - # - # doc.generate_pdf('report_functions', clean_tex=False) - - -# geometry_options = {"top": "2in", "bottom": "1in", "left": "0.6in", "right": "0.6in", "headsep": "0.8in"} -# doc = Document(geometry_options=geometry_options, indent=False) -# report_bolt_shear_check(doc) diff --git a/ResourceFiles/html_page/Makefile b/ResourceFiles/html_page/Makefile deleted file mode 100644 index ef959e038..000000000 --- a/ResourceFiles/html_page/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = Osdag_design_examples -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/doctrees/environment.pickle b/ResourceFiles/html_page/_build/doctrees/environment.pickle deleted file mode 100644 index 561078621..000000000 Binary files a/ResourceFiles/html_page/_build/doctrees/environment.pickle and /dev/null differ diff --git a/ResourceFiles/html_page/_build/doctrees/index.doctree b/ResourceFiles/html_page/_build/doctrees/index.doctree deleted file mode 100644 index da8d98146..000000000 Binary files a/ResourceFiles/html_page/_build/doctrees/index.doctree and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/.buildinfo b/ResourceFiles/html_page/_build/html/.buildinfo deleted file mode 100644 index 315aac292..000000000 --- a/ResourceFiles/html_page/_build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 09d45b43c56f94a68e06194a4538f6ed -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/ResourceFiles/html_page/_build/html/DesignReport_1.2.1.1.1.osi b/ResourceFiles/html_page/_build/html/DesignReport_1.2.1.1.1.osi deleted file mode 100644 index 189c95935..000000000 --- a/ResourceFiles/html_page/_build/html/DesignReport_1.2.1.1.1.osi +++ /dev/null @@ -1,68 +0,0 @@ -Bolt.Bolt_Hole_Type: Standard -Bolt.Diameter: -- '12' -- '16' -- '20' -- '24' -- '30' -- '36' -- '39' -- 08 -- '10' -- '14' -- '18' -- '22' -- '27' -- '33' -Bolt.Grade: -- '3.6' -- '4.6' -- '4.8' -- '5.6' -- '5.8' -- '6.8' -- '8.8' -- '9.8' -- '10.9' -- '12.9' -Bolt.Material_Grade_OverWrite: '410' -Bolt.Slip_Factor: '0.3' -Bolt.TensionType: Pretensioned -Bolt.Type: Bearing Bolt -Connector.Flange_Plate.Preferences: Outside + Inside -Connector.Flange_Plate.Thickness_list: &id001 -- '6' -- '8' -- '10' -- '12' -- '14' -- '16' -- '18' -- '20' -- '22' -- '24' -- '25' -- '26' -- '28' -- '30' -- '32' -- '36' -- '40' -- '45' -- '50' -- '56' -- '63' -- '80' -Connector.Material: E 250 (Fe 410 W)A -Connector.Web_Plate.Thickness_List: *id001 -Design.Design_Method: Limit State Design -Detailing.Corrosive_Influences: 'No' -Detailing.Edge_type: Sheared or hand flame cut -Detailing.Gap: '10' -Load.Axial: '100' -Load.Moment: '25' -Load.Shear: '50' -Material: E 250 (Fe 410 W)A -Member.Designation: WPB 200 X 200 X 34.65 -Member.Material: E 250 (Fe 410 W)A -Module: Beam Coverplate Connection diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.1.1.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.1.1.1.osi deleted file mode 100644 index e92a1a926..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.1.1.1.osi +++ /dev/null @@ -1,43 +0,0 @@ -Bolt.Bolt_Hole_Type: Standard -Bolt.Diameter: -- '12' -- '16' -- '20' -- '24' -- '30' -Bolt.Grade: -- '4.6' -- '4.8' -- '5.6' -- '6.8' -- '8.8' -Bolt.Slip_Factor: '0.3' -Bolt.TensionType: Pre-tensioned -Bolt.Type: Friction Grip Bolt -Connectivity: Column Flange-Beam Web -Connector.Material: E 350 (Fe 490) -Connector.Plate.Thickness_List: -- '10' -- '12' -- '16' -- '18' -- '20' -Design.Design_Method: Limit State Design -Detailing.Corrosive_Influences: 'No' -Detailing.Edge_type: Rolled, machine-flame cut, sawn and planed -Detailing.Gap: '15' -Load.Axial: '50' -Load.Shear: '180' -Material: E 250 (Fe 410 W)A -Member.Supported_Section.Designation: MB 350 -Member.Supported_Section.Material: E 250 (Fe 410 W)A -Member.Supporting_Section.Designation: HB 450 -Member.Supporting_Section.Material: E 250 (Fe 410 W)A -Module: Fin Plate Connection -Weld.Fab: Shop Weld -Weld.Material_Grade_OverWrite: '410' -out_titles_status: -- 1 -- 1 -- 1 -- 1 diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.1.1.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.1.1.2.osi deleted file mode 100644 index a80a46878..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.1.1.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "150"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "12"}, "Weld": {"Size (mm)": "8"}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5, "gap": 10.0}, "Member": {"ColumSection": "SC 200", "fu (MPa)": "410", "BeamSection": "MB 350", "fy (MPa)": "250", "Connectivity": "Column flange-Beam web"}, "Connection": "Finplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "4.6", "Diameter (mm)": "20", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 400, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.1.2.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.1.2.1.osi deleted file mode 100644 index 236b388f3..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.1.2.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "120"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "8"}, "Weld": {"Size (mm)": "8"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 10.0}, "Member": {"ColumSection": "PBP 300X180", "fu (MPa)": "410", "BeamSection": "UB 356 x 171 x 45", "fy (MPa)": "250", "Connectivity": "Column web-Beam web"}, "Connection": "Finplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "16", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Field weld", "fu_overwrite": "410", "safety_factor": 1.5}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.25, "bolt_fu": 800, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.1.2.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.1.2.2.osi deleted file mode 100644 index c5d830d7b..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.1.2.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "135"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "10"}, "Weld": {"Size (mm)": "8"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "Yes", "min_edgend_dist": 1.7, "gap": 10.0}, "Member": {"ColumSection": "SC 250", "fu (MPa)": "410", "BeamSection": "LB 300", "fy (MPa)": "250", "Connectivity": "Column web-Beam web"}, "Connection": "Finplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "4.8", "Diameter (mm)": "24", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.25, "bolt_fu": 420, "bolt_hole_type": "Standard"}} diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.1.3.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.1.3.1.osi deleted file mode 100644 index 00bbb4736..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.1.3.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "110"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "10"}, "Weld": {"Size (mm)": "8"}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5, "gap": 10.0}, "Member": {"ColumSection": "MB 350", "fu (MPa)": "410", "BeamSection": "NPB 270x135x36.1", "fy (MPa)": "250", "Connectivity": "Beam-Beam"}, "Connection": "Finplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 4, "slip_factor": 0.52, "bolt_fu": 1040, "bolt_hole_type": "Over-sized"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.1.3.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.1.3.2.osi deleted file mode 100644 index 62e143ba4..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.1.3.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "220"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "14"}, "Weld": {"Size (mm)": "10"}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5, "gap": 10.0}, "Member": {"ColumSection": "WPB 450x300x99.7", "fu (MPa)": "410", "BeamSection": "UB 356 x 171 x 67", "fy (MPa)": "250", "Connectivity": "Beam-Beam"}, "Connection": "Finplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "24", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.48, "bolt_fu": 1040, "bolt_hole_type": "Standard"}} diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.2.1.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.2.1.1.osi deleted file mode 100644 index 4f2a0cb4a..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.2.1.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "140"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "10"}, "Weld": {"Size (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 0.0}, "Member": {"ColumSection": "SC 250", "fu (MPa)": "410", "BeamSection": "MB 350", "fy (MPa)": "250", "Connectivity": "Column flange-Beam web"}, "Connection": "Endplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "3.6", "Diameter (mm)": "20", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "weld_fu": "410", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 330, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.2.1.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.2.1.2.osi deleted file mode 100644 index 2b998a1ae..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.2.1.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "195"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "12"}, "Weld": {"Size (mm)": "10"}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5, "gap": 0.0}, "Member": {"ColumSection": "UC 254 x 254 x 107", "fu (MPa)": "410", "BeamSection": "MB 300", "fy (MPa)": "250", "Connectivity": "Column flange-Beam web"}, "Connection": "Endplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "16", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "weld_fu": "410", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 800, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.2.2.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.2.2.1.osi deleted file mode 100644 index 93181c554..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.2.2.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "120"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "12"}, "Weld": {"Size (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 0.0}, "Member": {"ColumSection": "PBP 300X180", "fu (MPa)": "410", "BeamSection": "UB 356 x 171 x 45", "fy (MPa)": "250", "Connectivity": "Column web-Beam web"}, "Connection": "Endplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "16", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Field weld", "weld_fu": "410", "fu_overwrite": "410", "safety_factor": 1.5}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.25, "bolt_fu": 1040, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.2.2.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.2.2.2.osi deleted file mode 100644 index b136a7355..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.2.2.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "135"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "10"}, "Weld": {"Size (mm)": "10"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 0.0}, "Member": {"ColumSection": "SC 250", "fu (MPa)": "410", "BeamSection": "LB 300", "fy (MPa)": "250", "Connectivity": "Column web-Beam web"}, "Connection": "Endplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "4.8", "Diameter (mm)": "12", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "weld_fu": "410", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 1, "slip_factor": 0.3, "bolt_fu": 420, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.2.3.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.2.3.1.osi deleted file mode 100644 index 16a625d5f..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.2.3.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "160"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "16"}, "Weld": {"Size (mm)": "8"}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5, "gap": 0.0}, "Member": {"ColumSection": "MB 500", "fu (MPa)": "410", "BeamSection": "MB 400", "fy (MPa)": "250", "Connectivity": "Beam-Beam"}, "Connection": "Endplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "weld_fu": "410", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 4, "slip_factor": 0.2, "bolt_fu": 800, "bolt_hole_type": "Over-sized"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.2.3.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.2.3.2.osi deleted file mode 100644 index dad31ec5b..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.2.3.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "220"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "14"}, "Weld": {"Size (mm)": "12"}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5, "gap": 0.0}, "Member": {"ColumSection": "WPB 450x300x99.7", "fu (MPa)": "410", "BeamSection": "UB 356 x 171 x 67", "fy (MPa)": "250", "Connectivity": "Beam-Beam"}, "Connection": "Endplate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "24", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "weld_fu": "410", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.48, "bolt_fu": 1040, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.3.1.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.3.1.1.osi deleted file mode 100644 index cfa381e4c..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.3.1.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "140"}, "weld": {"typeof_weld": "Shop weld", "safety_factor": 1.25}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 10.0}, "cleat": {"section": "90 90 x 12", "Height (mm)": ""}, "Member": {"ColumSection": "SC 250", "fu (MPa)": "410", "BeamSection": "MB 400", "fy (MPa)": "250", "Connectivity": "Column flange-Beam web"}, "Connection": "cleatAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.48, "bolt_fu": 800, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.3.1.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.3.1.2.osi deleted file mode 100644 index 4c8d0239e..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.3.1.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "170"}, "weld": {"typeof_weld": "Shop weld", "safety_factor": 1.25}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "Yes", "min_edgend_dist": 1.5, "gap": 10.0}, "cleat": {"section": "100 100x 12", "Height (mm)": ""}, "Member": {"ColumSection": "HB 300", "fu (MPa)": "410", "BeamSection": "MB 350", "fy (MPa)": "250", "Connectivity": "Column flange-Beam web"}, "Connection": "cleatAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "4.6", "Diameter (mm)": "16", "Type": "Bearing Bolt"}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 800, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.3.2.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.3.2.1.osi deleted file mode 100644 index e51033c2a..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.3.2.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "120.5"}, "weld": {"typeof_weld": "Shop weld", "safety_factor": 1.25}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 5.0}, "cleat": {"section": "110 110 X 16", "Height (mm)": ""}, "Member": {"ColumSection": "UC 305 x 305 x 97", "fu (MPa)": "410", "BeamSection": "MB 350", "fy (MPa)": "250", "Connectivity": "Column web-Beam web"}, "Connection": "cleatAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.2, "bolt_fu": 800, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.3.2.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.3.2.2.osi deleted file mode 100644 index f6b0c69fd..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.3.2.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "80"}, "weld": {"typeof_weld": "Shop weld", "safety_factor": 1.25}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "Yes", "min_edgend_dist": 1.7, "gap": 10.0}, "cleat": {"section": "110 110 X 16", "Height (mm)": ""}, "Member": {"ColumSection": "UC 305 x 305 x 118", "fu (MPa)": "410", "BeamSection": "MB 200", "fy (MPa)": "250", "Connectivity": "Column web-Beam web"}, "Connection": "cleatAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "6.8", "Diameter (mm)": "12", "Type": "Bearing Bolt"}, "bolt": {"bolt_hole_clrnce": 1, "slip_factor": 0.3, "bolt_fu": 600, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.3.3.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.3.3.1.osi deleted file mode 100644 index 559ad0605..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.3.3.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "145"}, "weld": {"typeof_weld": "Shop weld", "safety_factor": 1.25}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 10.0}, "cleat": {"section": "100 100 x 10", "Height (mm)": ""}, "Member": {"ColumSection": "WB 450", "fu (MPa)": "410", "BeamSection": "MB 400", "fy (MPa)": "250", "Connectivity": "Beam-Beam"}, "Connection": "cleatAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "4.6", "Diameter (mm)": "24", "Type": "Bearing Bolt"}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 800, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.3.3.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.3.3.2.osi deleted file mode 100644 index 915cf2661..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.3.3.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "100"}, "weld": {"typeof_weld": "Shop weld", "safety_factor": 1.25}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5, "gap": 15.0}, "cleat": {"section": "100 100 x 8", "Height (mm)": ""}, "Member": {"ColumSection": "WPB 280x280x61.2", "fu (MPa)": "410", "BeamSection": "NPB 220x110x29.4", "fy (MPa)": "250", "Connectivity": "Beam-Beam"}, "Connection": "cleatAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "bolt": {"bolt_hole_clrnce": 4, "slip_factor": 0.52, "bolt_fu": 800, "bolt_hole_type": "Over-sized"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.4.1.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.4.1.1.osi deleted file mode 100644 index f1a36a296..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.4.1.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "100"}, "Angle": {"TopAngleSection": "150 150 X 10", "AngleSection": "150 150 X 15"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 10.0}, "Member": {"fu (MPa)": "410", "Connectivity": "Column flange-Beam flange", "ColumnSection": "UC 203 x 203 x 86", "BeamSection": "MB 300", "fy (MPa)": "250"}, "Connection": "SeatedAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "bolt": {"slip_factor": 0.55, "bolt_fu": 940, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.4.1.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.4.1.2.osi deleted file mode 100644 index 01fa39b18..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.4.1.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "140"}, "Angle": {"TopAngleSection": "90 90 x 8", "AngleSection": "110 110 X 16"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "Yes", "min_edgend_dist": 1.7, "gap": 10.0}, "Member": {"fu (MPa)": "410", "Connectivity": "Column flange-Beam flange", "ColumnSection": "SC 140", "BeamSection": "MB 200", "fy (MPa)": "250"}, "Connection": "SeatedAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "6.8", "Diameter (mm)": "12", "Type": "Bearing Bolt"}, "bolt": {"slip_factor": 0.3, "bolt_fu": 800, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.4.2.1.osi b/ResourceFiles/html_page/_build/html/Example_1.1.4.2.1.osi deleted file mode 100644 index 883d5d423..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.4.2.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "80"}, "Angle": {"TopAngleSection": "90 90 x 10", "AngleSection": "150 150 X 15"}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5, "gap": 10.0}, "Member": {"fu (MPa)": "410", "Connectivity": "Column web-Beam flange", "ColumnSection": "PBP 300X180", "BeamSection": "NPB 250x150x39.8", "fy (MPa)": "250"}, "Connection": "SeatedAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "5.8", "Diameter (mm)": "16", "Type": "Bearing Bolt"}, "bolt": {"slip_factor": 0.3, "bolt_fu": 800, "bolt_hole_type": "Standard"}} diff --git a/ResourceFiles/html_page/_build/html/Example_1.1.4.2.2.osi b/ResourceFiles/html_page/_build/html/Example_1.1.4.2.2.osi deleted file mode 100644 index 261985d8b..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.1.4.2.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"ShearForce (kN)": "140"}, "Angle": {"TopAngleSection": "90 90 x 10", "AngleSection": "150 150 X 15"}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5, "gap": 5.0}, "Member": {"fu (MPa)": "410", "Connectivity": "Column web-Beam flange", "ColumnSection": "HB 200", "BeamSection": "WPB 140x140x24.7", "fy (MPa)": "250"}, "Connection": "SeatedAngle", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "5.8", "Diameter (mm)": "12", "Type": "Bearing Bolt"}, "bolt": {"slip_factor": 0.3, "bolt_fu": 800, "bolt_hole_type": "Over-sized"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.1.1.1.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.1.1.1.1.osi deleted file mode 100644 index cc4d908c9..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.1.1.1.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "175", "ShearForce (kN)": "115", "AxialForce": "100"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 5.0}, "Connection": "Coverplate", "FlangePlate": {"Width (mm)": "", "Height (mm)": "", "Preferences": "Outside + Inside", "Thickness (mm)": "16"}, "Member": {"Connectivity": "Bolted", "fu (MPa)": "410", "BeamSection": "UB 457 x 191 x 82", "fy (MPa)": "250"}, "WebPlate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "10"}, "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 800.0, "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.1.1.1.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.1.1.1.2.osi deleted file mode 100644 index 835afb73a..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.1.1.1.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "150", "ShearForce (kN)": "100", "AxialForce": "75"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7, "gap": 5.0}, "Connection": "Coverplate", "FlangePlate": {"Width (mm)": "", "Height (mm)": "", "Preferences": "Outside", "Thickness (mm)": "14"}, "Member": {"Connectivity": "Bolted", "fu (MPa)": "410", "BeamSection": "UB 406 x 178 x 67", "fy (MPa)": "250"}, "WebPlate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "6"}, "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 1000.0, "bolt_hole_type": "Standard"}} diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.1.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.1.2.1.1.osi deleted file mode 100644 index 4d2f172e7..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.1.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "200", "ShearForce (kN)": "150", "AxialForce (kN)": 0}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "20"}, "Weld": {"Flange (mm)": "3", "Type": "Groove Weld (CJP)", "Web (mm)": "3"}, "detailing": {"typeof_edge": "b - Rolled, machine-flame cut, sawn and planed", "is_env_corrosive": "No", "min_edgend_dist": 1.5}, "Member": {"Connectivity": "Extended both ways", "fu (MPa)": "450", "BeamSection": "NPB 350x170x50.2", "fy (MPa)": "230"}, "Connection": "Extended", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "9.8", "Diameter (mm)": "20", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "450", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 900.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.1.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.1.2.1.2.osi deleted file mode 100644 index 0ff8bd69a..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.1.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "350", "ShearForce (kN)": "120", "AxialForce (kN)": "50"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "20"}, "Weld": {"Flange (mm)": "3", "Type": "Groove Weld (CJP)", "Web (mm)": "3"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"Connectivity": "Extended both ways", "fu (MPa)": "410", "BeamSection": "UB 406 x 178 x 74", "fy (MPa)": "250"}, "Connection": "Extended", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "16", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.48, "bolt_fu": 800.0, "bolt_type": "Pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.2.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.1.2.2.1.osi deleted file mode 100644 index 8e93e3833..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.2.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "75", "ShearForce (kN)": "12", "AxialForce (kN)": "00"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "16"}, "Weld": {"Flange (mm)": "3", "Type": "Groove Weld (CJP)", "Web (mm)": "3"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"Connectivity": "Extended one way", "fu (MPa)": "410", "BeamSection": "MB 400", "fy (MPa)": "250"}, "Connection": "Extended", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "3.6", "Diameter (mm)": "30", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 8, "slip_factor": 0.3, "bolt_fu": 300.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Over-sized"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.2.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.1.2.2.2.osi deleted file mode 100644 index a39d8ce6b..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.2.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "75", "ShearForce (kN)": "12", "AxialForce (kN)": "00"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "14"}, "Weld": {"Flange (mm)": "3", "Type": "Groove Weld (CJP)", "Web (mm)": "3"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"Connectivity": "Extended one way", "fu (MPa)": "410", "BeamSection": "MB 400", "fy (MPa)": "250"}, "Connection": "Extended", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "3.6", "Diameter (mm)": "20", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 4, "slip_factor": 0.3, "bolt_fu": 300.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Over-sized"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.3.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.1.2.3.1.osi deleted file mode 100644 index 36a08f36e..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.3.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "50", "ShearForce (kN)": "25", "AxialForce (kN)": "00"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "22"}, "Weld": {"Flange (mm)": "6", "Type": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"Connectivity": "Flush", "fu (MPa)": "410", "BeamSection": "MB 300", "fy (MPa)": "250"}, "Connection": "Extended", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "24", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 6, "slip_factor": 0.3, "bolt_fu": 800.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Over-sized"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.3.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.1.2.3.2.osi deleted file mode 100644 index 664e9d26b..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.1.2.3.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "50", "ShearForce (kN)": "25", "AxialForce (kN)": "00"}, "Plate": {"Width (mm)": "", "Height (mm)": "", "Thickness (mm)": "20"}, "Weld": {"Flange (mm)": "6", "Type": "Fillet Weld", "Web (mm)": "5"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"Connectivity": "Flush", "fu (MPa)": "410", "BeamSection": "MB 300", "fy (MPa)": "250"}, "Connection": "Extended", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 4, "slip_factor": 0.3, "bolt_fu": 800.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Over-sized"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.1.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.1.1.osi deleted file mode 100644 index ddfe85f3d..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.1.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "25", "ShearForce (kN)": "35", "AxialForce (kN)": "12"}, "Plate": {"Thickness (mm)": "26"}, "Weld": {"Flange (mm)": "10", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column flange-Beam web", "BeamSection": "WPB 300x300x96.8", "fy (MPa)": "250", "EndPlate_type": "Extended both ways", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "12.9", "Diameter (mm)": "30", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 3, "slip_factor": 0.3, "bolt_fu": 1200.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.1.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.1.2.osi deleted file mode 100644 index ba8edde56..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.1.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "25", "ShearForce (kN)": "35", "AxialForce (kN)": "120"}, "Plate": {"Thickness (mm)": "26"}, "Weld": {"Flange (mm)": "8", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column flange-Beam web", "BeamSection": "WPB 300x300x96.8", "fy (MPa)": "250", "EndPlate_type": "Extended both ways", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "12.9", "Diameter (mm)": "30", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 3, "slip_factor": 0.3, "bolt_fu": 1200.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.2.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.2.1.osi deleted file mode 100644 index 4e86cb2b8..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.2.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "12", "ShearForce (kN)": "150", "AxialForce (kN)": "50"}, "Plate": {"Thickness (mm)": "26"}, "Weld": {"Flange (mm)": "10", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column web-Beam web", "BeamSection": "WPB 240x240x60.3", "fy (MPa)": "250", "EndPlate_type": "Extended both ways", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "30", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 3, "slip_factor": 0.3, "bolt_fu": 1000.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.2.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.2.2.osi deleted file mode 100644 index afc1413a7..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.1.2.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "12", "ShearForce (kN)": "150", "AxialForce (kN)": "50"}, "Plate": {"Thickness (mm)": "26"}, "Weld": {"Flange (mm)": "10", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column web-Beam web", "BeamSection": "WPB 240x240x60.3", "fy (MPa)": "250", "EndPlate_type": "Extended both ways", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 1000.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.1.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.1.1.osi deleted file mode 100644 index 6bdad3a7f..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.1.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "25", "ShearForce (kN)": "35", "AxialForce (kN)": "12"}, "Plate": {"Thickness (mm)": "26"}, "Weld": {"Flange (mm)": "10", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column flange-Beam web", "BeamSection": "WPB 300x300x96.8", "fy (MPa)": "250", "EndPlate_type": "Extended one way", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "24", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 1000.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.1.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.1.2.osi deleted file mode 100644 index be8a1e5b3..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.1.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "25", "ShearForce (kN)": "35", "AxialForce (kN)": "120"}, "Plate": {"Thickness (mm)": "26"}, "Weld": {"Flange (mm)": "10", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column flange-Beam web", "BeamSection": "WPB 300x300x96.8", "fy (MPa)": "250", "EndPlate_type": "Extended one way", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "24", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 1000.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.2.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.2.1.osi deleted file mode 100644 index ef060f613..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.2.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "12", "ShearForce (kN)": "150", "AxialForce (kN)": "50"}, "Plate": {"Thickness (mm)": "24"}, "Weld": {"Flange (mm)": "8", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column web-Beam web", "BeamSection": "WPB 240x240x60.3", "fy (MPa)": "250", "EndPlate_type": "Extended one way", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "24", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 1000.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.2.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.2.2.osi deleted file mode 100644 index 89b1c609e..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.2.2.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "12", "ShearForce (kN)": "150", "AxialForce (kN)": "50"}, "Plate": {"Thickness (mm)": "26"}, "Weld": {"Flange (mm)": "8", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column web-Beam web", "BeamSection": "WPB 240x240x60.3", "fy (MPa)": "250", "EndPlate_type": "Extended one way", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 1000.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.1.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.1.1.osi deleted file mode 100644 index 4ba5d4e0c..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.1.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "25", "ShearForce (kN)": "35", "AxialForce (kN)": "12"}, "Plate": {"Thickness (mm)": "24"}, "Weld": {"Flange (mm)": "10", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column flange-Beam web", "BeamSection": "WPB 300x300x96.8", "fy (MPa)": "250", "EndPlate_type": "Flush end plate", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "12.9", "Diameter (mm)": "30", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 3, "slip_factor": 0.3, "bolt_fu": 1200.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.1.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.1.2.osi deleted file mode 100644 index 66009add8..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.1.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "25", "ShearForce (kN)": "35", "AxialForce (kN)": "12"}, "Plate": {"Thickness (mm)": "24"}, "Weld": {"Flange (mm)": "10", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column flange-Beam web", "BeamSection": "WPB 300x300x96.8", "fy (MPa)": "250", "EndPlate_type": "Flush end plate", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "20", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 1000.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.2.1.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.2.1.osi deleted file mode 100644 index bdc77b749..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.2.1.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "15", "ShearForce (kN)": "20", "AxialForce (kN)": "15"}, "Plate": {"Thickness (mm)": "20"}, "Weld": {"Flange (mm)": "16", "Method": "Groove Weld (CJP)", "Web (mm)": "10"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column web-Beam web", "BeamSection": "NPB 160x80x15.8", "fy (MPa)": "250", "EndPlate_type": "Flush end plate", "ColumnSection": "UC 305 x 305 x 97"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "8.8", "Diameter (mm)": "16", "Type": "Bearing Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 800.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.2.2.osi b/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.2.2.osi deleted file mode 100644 index c8fac19d6..000000000 --- a/ResourceFiles/html_page/_build/html/Example_1.2.2.1.3.2.2.osi +++ /dev/null @@ -1 +0,0 @@ -{"Load": {"Moment (kNm)": "75", "ShearForce (kN)": "50", "AxialForce (kN)": "25"}, "Plate": {"Thickness (mm)": "24"}, "Weld": {"Flange (mm)": "6", "Method": "Fillet Weld", "Web (mm)": "6"}, "detailing": {"typeof_edge": "a - Sheared or hand flame cut", "is_env_corrosive": "No", "min_edgend_dist": 1.7}, "Member": {"fu (MPa)": "410", "Connectivity": "Column web-Beam web", "BeamSection": "WPB 240x240x60.3", "fy (MPa)": "250", "EndPlate_type": "Flush end plate", "ColumnSection": "UC 305 x 305 x 137"}, "Connection": "BCEndPlate", "design": {"design_method": "Limit State Design"}, "Bolt": {"Grade": "10.9", "Diameter (mm)": "20", "Type": "Friction Grip Bolt"}, "weld": {"typeof_weld": "Shop weld", "fu_overwrite": "410", "safety_factor": 1.25}, "bolt": {"bolt_hole_clrnce": 2, "slip_factor": 0.3, "bolt_fu": 1000.0, "bolt_type": "Non-pretensioned", "bolt_hole_type": "Standard"}} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/_images/website_header.png b/ResourceFiles/html_page/_build/html/_images/website_header.png deleted file mode 100644 index f66ec158a..000000000 Binary files a/ResourceFiles/html_page/_build/html/_images/website_header.png and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/_static/ajax-loader.gif b/ResourceFiles/html_page/_build/html/_static/ajax-loader.gif deleted file mode 100644 index 61faf8cab..000000000 Binary files a/ResourceFiles/html_page/_build/html/_static/ajax-loader.gif and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/_static/alabaster.css b/ResourceFiles/html_page/_build/html/_static/alabaster.css deleted file mode 100644 index be65b1374..000000000 --- a/ResourceFiles/html_page/_build/html/_static/alabaster.css +++ /dev/null @@ -1,693 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro', serif; - font-size: 17px; - background-color: #fff; - color: #000; - margin: 0; - padding: 0; -} - - -div.document { - width: 940px; - margin: 30px auto 0 auto; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 220px; -} - -div.sphinxsidebar { - width: 220px; - font-size: 14px; - line-height: 1.5; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #fff; - color: #3E4349; - padding: 0 30px 0 30px; -} - -div.body > .section { - text-align: left; -} - -div.footer { - width: 940px; - margin: 20px auto 30px auto; - font-size: 14px; - color: #888; - text-align: right; -} - -div.footer a { - color: #888; -} - -p.caption { - font-family: inherit; - font-size: inherit; -} - - -div.relations { - display: none; -} - - -div.sphinxsidebar a { - color: #444; - text-decoration: none; - border-bottom: 1px dotted #999; -} - -div.sphinxsidebar a:hover { - border-bottom: 1px solid #999; -} - -div.sphinxsidebarwrapper { - padding: 18px 10px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 0; - margin: -10px 0 0 0px; - text-align: center; -} - -div.sphinxsidebarwrapper h1.logo { - margin-top: -10px; - text-align: center; - margin-bottom: 5px; - text-align: left; -} - -div.sphinxsidebarwrapper h1.logo-name { - margin-top: 0px; -} - -div.sphinxsidebarwrapper p.blurb { - margin-top: 0; - font-style: normal; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: 'Garamond', 'Georgia', serif; - color: #444; - font-size: 24px; - font-weight: normal; - margin: 0 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 20px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p.logo a, -div.sphinxsidebar h3 a, -div.sphinxsidebar p.logo a:hover, -div.sphinxsidebar h3 a:hover { - border: none; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar ul li.toctree-l1 > a { - font-size: 120%; -} - -div.sphinxsidebar ul li.toctree-l2 > a { - font-size: 110%; -} - -div.sphinxsidebar input { - border: 1px solid #CCC; - font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro', serif; - font-size: 1em; -} - -div.sphinxsidebar hr { - border: none; - height: 1px; - color: #AAA; - background: #AAA; - - text-align: left; - margin-left: 0; - width: 50%; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #DDD; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #EAEAEA; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - margin: 20px 0px; - padding: 10px 30px; - background-color: #EEE; - border: 1px solid #CCC; -} - -div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fafafa; -} - -div.admonition p.admonition-title { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight { - background-color: #fff; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.warning { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.danger { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.error { - background-color: #FCC; - border: 1px solid #FAA; - -moz-box-shadow: 2px 2px 4px #D52C2C; - -webkit-box-shadow: 2px 2px 4px #D52C2C; - box-shadow: 2px 2px 4px #D52C2C; -} - -div.caution { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.attention { - background-color: #FCC; - border: 1px solid #FAA; -} - -div.important { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.note { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.tip { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.hint { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.seealso { - background-color: #EEE; - border: 1px solid #CCC; -} - -div.topic { - background-color: #EEE; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt, code { - font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.9em; -} - -.hll { - background-color: #FFC; - margin: 0 -12px; - padding: 0 12px; - display: block; -} - -img.screenshot { -} - -tt.descname, tt.descclassname, code.descname, code.descclassname { - font-size: 0.95em; -} - -tt.descname, code.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #EEE; - -webkit-box-shadow: 2px 2px 4px #EEE; - box-shadow: 2px 2px 4px #EEE; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #EEE; - background: #FDFDFD; - font-size: 0.9em; -} - -table.footnote + table.footnote { - margin-top: -15px; - border-top: none; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.field-list p { - margin-bottom: 0.8em; -} - -/* Cloned from - * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 - */ -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -table.footnote td.label { - width: .1px; - padding: 0.3em 0 0.3em 0.5em; -} - -table.footnote td { - padding: 0.3em 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -blockquote { - margin: 0 0 0 30px; - padding: 0; -} - -ul, ol { - /* Matches the 30px from the narrow-screen "li > ul" selector below */ - margin: 10px 0 10px 30px; - padding: 0; -} - -pre { - background: #EEE; - padding: 7px 30px; - margin: 15px 0px; - line-height: 1.3em; -} - -div.viewcode-block:target { - background: #ffd; -} - -dl pre, blockquote pre, li pre { - margin-left: 0; - padding-left: 30px; -} - -tt, code { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, code.xref, a tt { - background-color: #FBFBFB; - border-bottom: 1px solid #fff; -} - -a.reference { - text-decoration: none; - border-bottom: 1px dotted #004B6B; -} - -/* Don't put an underline on images */ -a.image-reference, a.image-reference:hover { - border-bottom: none; -} - -a.reference:hover { - border-bottom: 1px solid #6D4100; -} - -a.footnote-reference { - text-decoration: none; - font-size: 0.7em; - vertical-align: top; - border-bottom: 1px dotted #004B6B; -} - -a.footnote-reference:hover { - border-bottom: 1px solid #6D4100; -} - -a:hover tt, a:hover code { - background: #EEE; -} - - -@media screen and (max-width: 870px) { - - div.sphinxsidebar { - display: none; - } - - div.document { - width: 100%; - - } - - div.documentwrapper { - margin-left: 0; - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - } - - div.bodywrapper { - margin-top: 0; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; - } - - ul { - margin-left: 0; - } - - li > ul { - /* Matches the 30px from the "ul, ol" selector above */ - margin-left: 30px; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .bodywrapper { - margin: 0; - } - - .footer { - width: auto; - } - - .github { - display: none; - } - - - -} - - - -@media screen and (max-width: 875px) { - - body { - margin: 0; - padding: 20px 30px; - } - - div.documentwrapper { - float: none; - background: #fff; - } - - div.sphinxsidebar { - display: block; - float: none; - width: 102.5%; - margin: 50px -30px -20px -30px; - padding: 10px 20px; - background: #333; - color: #FFF; - } - - div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, - div.sphinxsidebar h3 a { - color: #fff; - } - - div.sphinxsidebar a { - color: #AAA; - } - - div.sphinxsidebar p.logo { - display: none; - } - - div.document { - width: 100%; - margin: 0; - } - - div.footer { - display: none; - } - - div.bodywrapper { - margin: 0; - } - - div.body { - min-height: 0; - padding: 0; - } - - .rtd_doc_footer { - display: none; - } - - .document { - width: auto; - } - - .footer { - width: auto; - } - - .footer { - width: auto; - } - - .github { - display: none; - } -} - - -/* misc. */ - -.revsys-inline { - display: none!important; -} - -/* Make nested-list/multi-paragraph items look better in Releases changelog - * pages. Without this, docutils' magical list fuckery causes inconsistent - * formatting between different release sub-lists. - */ -div#changelog > div.section > ul > li > p:only-child { - margin-bottom: 0; -} - -/* Hide fugly table cell borders in ..bibliography:: directive output */ -table.docutils.citation, table.docutils.citation td, table.docutils.citation th { - border: none; - /* Below needed in some edge cases; if not applied, bottom shadows appear */ - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/_static/basic.css b/ResourceFiles/html_page/_build/html/_static/basic.css deleted file mode 100644 index 56f5efc61..000000000 --- a/ResourceFiles/html_page/_build/html/_static/basic.css +++ /dev/null @@ -1,835 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 450px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a.brackets:before, -span.brackets > a:before{ - content: "["; -} - -a.brackets:after, -span.brackets > a:after { - content: "]"; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -div.admonition, div.topic, pre, div[class|="highlight"] { - clear: both; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; - overflow-x: auto; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; - overflow-x: auto; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -ol > li:first-child > :first-child, -ul > li:first-child > :first-child { - margin-top: 0px; -} - -ol ol > li:first-child > :first-child, -ol ul > li:first-child > :first-child, -ul ol > li:first-child > :first-child, -ul ul > li:first-child > :first-child { - margin-top: revert; -} - -ol > li:last-child > :last-child, -ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol ol > li:last-child > :last-child, -ol ul > li:last-child > :last-child, -ul ol > li:last-child > :last-child, -ul ul > li:last-child > :last-child { - margin-bottom: revert; -} - -dl.footnote > dt, -dl.citation > dt { - float: left; - margin-right: 0.5em; -} - -dl.footnote > dd, -dl.citation > dd { - margin-bottom: 0em; -} - -dl.footnote > dd:after, -dl.citation > dd:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dt:after { - content: ":"; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0.5em; - content: ":"; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; -} - -div[class^="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -code.descclassname { - background-color: transparent; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/_static/classic.css b/ResourceFiles/html_page/_build/html/_static/classic.css deleted file mode 100644 index 0ac8a0889..000000000 --- a/ResourceFiles/html_page/_build/html/_static/classic.css +++ /dev/null @@ -1,266 +0,0 @@ -/* - * classic.css_t - * ~~~~~~~~~~~~~ - * - * Sphinx stylesheet -- classic theme. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -html { - /* CSS hack for macOS's scrollbar (see #1125) */ - background-color: #FFFFFF; -} - -body { - font-family: sans-serif; - font-size: 100%; - background-color: #11303d; - color: #000; - margin: 0; - padding: 0; -} - -div.document { - background-color: YellowGreen ; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 230px; -} - -div.body { - background-color: #ffffff; - color: #000000; - padding: 0 20px 30px 20px; -} - -div.footer { - color: #ffffff; - width: 100%; - padding: 9px 0 9px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #ffffff; - text-decoration: underline; -} - -div.related { - background-color: YellowGreen; - line-height: 30px; - color: #ffffff; -} - -div.related a { - color: #ffffff; -} - -div.sphinxsidebar { -} - -div.sphinxsidebar h3 { - font-family: 'Trebuchet MS', sans-serif; - color: black; - font-size: 1.4em; - font-weight: normal; - margin: 0; - padding: 0; -} - -div.sphinxsidebar h3 a { - color: black; -} - -div.sphinxsidebar h4 { - font-family: 'Trebuchet MS', sans-serif; - color: black; - font-size: 1.3em; - font-weight: normal; - margin: 5px 0 0 0; - padding: 0; -} - -div.sphinxsidebar p { - color: black; -} - -div.sphinxsidebar p.topless { - margin: 5px 10px 10px 10px; -} - -div.sphinxsidebar ul { - margin: 10px; - padding: 0; - color: black; -} - -div.sphinxsidebar a { - color: black; -} - -div.sphinxsidebar input { - border: 1px solid black; - font-family: sans-serif; - font-size: 1em; -} - - - -/* -- hyperlink styles ------------------------------------------------------ */ - -a { - color: #355f7c; - text-decoration: none; -} - -a:visited { - color: #355f7c; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - - - -/* -- body styles ----------------------------------------------------------- */ - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Trebuchet MS', sans-serif; - background-color: #f2f2f2; - font-weight: normal; - color: #20435c; - border-bottom: 1px solid #ccc; - margin: 20px -20px 10px -20px; - padding: 3px 0 3px 10px; -} - -div.body h1 { margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 160%; } -div.body h3 { font-size: 140%; } -div.body h4 { font-size: 120%; } -div.body h5 { font-size: 110%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - text-align: justify; - line-height: 130%; -} - -div.admonition p.admonition-title + p { - display: inline; -} - -div.admonition p { - margin-bottom: 5px; -} - -div.admonition pre { - margin-bottom: 5px; -} - -div.admonition ul, div.admonition ol { - margin-bottom: 5px; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre { - padding: 5px; - background-color: unset; - color: unset; - line-height: 120%; - border: 1px solid #ac9; - border-left: none; - border-right: none; -} - -code { - background-color: #ecf0f3; - padding: 0 1px 0 1px; - font-size: 0.95em; -} - -th, dl.field-list > dt { - background-color: #ede; -} - -.warning code { - background: #efc2c2; -} - -.note code { - background: #d6d6d6; -} - -.viewcode-back { - font-family: sans-serif; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #ac9; - border-bottom: 1px solid #ac9; -} - -div.code-block-caption { - color: #efefef; - background-color: #1c4e63; -} \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/_static/comment-bright.png b/ResourceFiles/html_page/_build/html/_static/comment-bright.png deleted file mode 100644 index 15e27edb1..000000000 Binary files a/ResourceFiles/html_page/_build/html/_static/comment-bright.png and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/_static/comment-close.png b/ResourceFiles/html_page/_build/html/_static/comment-close.png deleted file mode 100644 index 4d91bcf57..000000000 Binary files a/ResourceFiles/html_page/_build/html/_static/comment-close.png and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/_static/comment.png b/ResourceFiles/html_page/_build/html/_static/comment.png deleted file mode 100644 index dfbc0cbd5..000000000 Binary files a/ResourceFiles/html_page/_build/html/_static/comment.png and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/_static/custom.css b/ResourceFiles/html_page/_build/html/_static/custom.css deleted file mode 100644 index 2a924f1d6..000000000 --- a/ResourceFiles/html_page/_build/html/_static/custom.css +++ /dev/null @@ -1 +0,0 @@ -/* This file intentionally left blank. */ diff --git a/ResourceFiles/html_page/_build/html/_static/doctools.js b/ResourceFiles/html_page/_build/html/_static/doctools.js deleted file mode 100644 index daccd209d..000000000 --- a/ResourceFiles/html_page/_build/html/_static/doctools.js +++ /dev/null @@ -1,315 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for all documentation. - * - * :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/** - * select a different prefix for underscore - */ -$u = _.noConflict(); - -/** - * make the code below compatible with browsers without - * an installed firebug like debugger -if (!window.console || !console.firebug) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", - "profile", "profileEnd"]; - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {}; -} - */ - -/** - * small helper function to urldecode strings - */ -jQuery.urldecode = function(x) { - return decodeURIComponent(x).replace(/\+/g, ' '); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { - this.initOnKeyListeners(); - } - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated === 'undefined') - return string; - return (typeof translated === 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated === 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - if (!body.length) { - body = $('body'); - } - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) === 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this === '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - }, - - initOnKeyListeners: function() { - $(document).keydown(function(event) { - var activeElementType = document.activeElement.tagName; - // don't navigate when in search box or textarea - if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' - && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) { - switch (event.keyCode) { - case 37: // left - var prevHref = $('link[rel="prev"]').prop('href'); - if (prevHref) { - window.location.href = prevHref; - return false; - } - case 39: // right - var nextHref = $('link[rel="next"]').prop('href'); - if (nextHref) { - window.location.href = nextHref; - return false; - } - } - } - }); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/ResourceFiles/html_page/_build/html/_static/documentation_options.js b/ResourceFiles/html_page/_build/html/_static/documentation_options.js deleted file mode 100644 index 344db3bca..000000000 --- a/ResourceFiles/html_page/_build/html/_static/documentation_options.js +++ /dev/null @@ -1,12 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '2017.06.0.2', - LANGUAGE: 'None', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false -}; \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/_static/down-pressed.png b/ResourceFiles/html_page/_build/html/_static/down-pressed.png deleted file mode 100644 index 5756c8cad..000000000 Binary files a/ResourceFiles/html_page/_build/html/_static/down-pressed.png and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/_static/down.png b/ResourceFiles/html_page/_build/html/_static/down.png deleted file mode 100644 index 1b3bdad2c..000000000 Binary files a/ResourceFiles/html_page/_build/html/_static/down.png and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/_static/file.png b/ResourceFiles/html_page/_build/html/_static/file.png deleted file mode 100644 index a858a410e..000000000 Binary files a/ResourceFiles/html_page/_build/html/_static/file.png and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/_static/jquery-3.2.1.js b/ResourceFiles/html_page/_build/html/_static/jquery-3.2.1.js deleted file mode 100644 index d2d8ca479..000000000 --- a/ResourceFiles/html_page/_build/html/_static/jquery-3.2.1.js +++ /dev/null @@ -1,10253 +0,0 @@ -/*! - * jQuery JavaScript Library v3.2.1 - * https://jquery.com/ - * - * Includes Sizzle.js - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://jquery.org/license - * - * Date: 2017-03-20T18:59Z - */ -( function( global, factory ) { - - "use strict"; - - if ( typeof module === "object" && typeof module.exports === "object" ) { - - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get jQuery. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info. - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 -// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode -// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common -// enough that all such attempts are guarded in a try block. -"use strict"; - -var arr = []; - -var document = window.document; - -var getProto = Object.getPrototypeOf; - -var slice = arr.slice; - -var concat = arr.concat; - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var fnToString = hasOwn.toString; - -var ObjectFunctionString = fnToString.call( Object ); - -var support = {}; - - - - function DOMEval( code, doc ) { - doc = doc || document; - - var script = doc.createElement( "script" ); - - script.text = code; - doc.head.appendChild( script ).parentNode.removeChild( script ); - } -/* global Symbol */ -// Defining this global in .eslintrc.json would create a danger of using the global -// unguarded in another place, it seems safer to define global only for this module - - - -var - version = "3.2.1", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }, - - // Support: Android <=4.0 only - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([a-z])/g, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; - -jQuery.fn = jQuery.prototype = { - - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - - // Return all the elements in a clean array - if ( num == null ) { - return slice.call( this ); - } - - // Return just the one element from the set - return num < 0 ? this[ num + this.length ] : this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - each: function( callback ) { - return jQuery.each( this, callback ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map( this, function( elem, i ) { - return callback.call( elem, i, elem ); - } ) ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[ 0 ] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // Skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { - target = {}; - } - - // Extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - - // Only deal with non-null/undefined values - if ( ( options = arguments[ i ] ) != null ) { - - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject( copy ) || - ( copyIsArray = Array.isArray( copy ) ) ) ) { - - if ( copyIsArray ) { - copyIsArray = false; - clone = src && Array.isArray( src ) ? src : []; - - } else { - clone = src && jQuery.isPlainObject( src ) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend( { - - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - isFunction: function( obj ) { - return jQuery.type( obj ) === "function"; - }, - - isWindow: function( obj ) { - return obj != null && obj === obj.window; - }, - - isNumeric: function( obj ) { - - // As of jQuery 3.0, isNumeric is limited to - // strings and numbers (primitives or objects) - // that can be coerced to finite numbers (gh-2662) - var type = jQuery.type( obj ); - return ( type === "number" || type === "string" ) && - - // parseFloat NaNs numeric-cast false positives ("") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - !isNaN( obj - parseFloat( obj ) ); - }, - - isPlainObject: function( obj ) { - var proto, Ctor; - - // Detect obvious negatives - // Use toString instead of jQuery.type to catch host objects - if ( !obj || toString.call( obj ) !== "[object Object]" ) { - return false; - } - - proto = getProto( obj ); - - // Objects with no prototype (e.g., `Object.create( null )`) are plain - if ( !proto ) { - return true; - } - - // Objects with prototype are plain iff they were constructed by a global Object function - Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; - return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; - }, - - isEmptyObject: function( obj ) { - - /* eslint-disable no-unused-vars */ - // See https://github.com/eslint/eslint/issues/6125 - var name; - - for ( name in obj ) { - return false; - } - return true; - }, - - type: function( obj ) { - if ( obj == null ) { - return obj + ""; - } - - // Support: Android <=2.3 only (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call( obj ) ] || "object" : - typeof obj; - }, - - // Evaluates a script in a global context - globalEval: function( code ) { - DOMEval( code ); - }, - - // Convert dashed to camelCase; used by the css and data modules - // Support: IE <=9 - 11, Edge 12 - 13 - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - each: function( obj, callback ) { - var length, i = 0; - - if ( isArrayLike( obj ) ) { - length = obj.length; - for ( ; i < length; i++ ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } else { - for ( i in obj ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } - - return obj; - }, - - // Support: Android <=4.0 only - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArrayLike( Object( arr ) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var length, value, - i = 0, - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArrayLike( elems ) ) { - length = elems.length; - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - now: Date.now, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -} ); - -if ( typeof Symbol === "function" ) { - jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; -} - -// Populate the class2type map -jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), -function( i, name ) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -} ); - -function isArrayLike( obj ) { - - // Support: real iOS 8.2 only (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = !!obj && "length" in obj && obj.length, - type = jQuery.type( obj ); - - if ( type === "function" || jQuery.isWindow( obj ) ) { - return false; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v2.3.3 - * https://sizzlejs.com/ - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2016-08-08 - */ -(function( window ) { - -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + 1 * new Date(), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf as it's faster than native - // https://jsperf.com/thor-indexof-vs-for/5 - indexOf = function( list, elem ) { - var i = 0, - len = list.length; - for ( ; i < len; i++ ) { - if ( list[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - - // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", - - pseudos = ":(" + identifier + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rwhitespace = new RegExp( whitespace + "+", "g" ), - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + identifier + ")" ), - "CLASS": new RegExp( "^\\.(" + identifier + ")" ), - "TAG": new RegExp( "^(" + identifier + "|[*])" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - - // CSS escapes - // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // CSS string/identifier serialization - // https://drafts.csswg.org/cssom/#common-serializing-idioms - rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, - fcssescape = function( ch, asCodePoint ) { - if ( asCodePoint ) { - - // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER - if ( ch === "\0" ) { - return "\uFFFD"; - } - - // Control characters and (dependent upon position) numbers get escaped as code points - return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; - } - - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; - }, - - // Used for iframes - // See setDocument() - // Removing the function wrapper causes a "Permission Denied" - // error in IE - unloadHandler = function() { - setDocument(); - }, - - disabledAncestor = addCombinator( - function( elem ) { - return elem.disabled === true && ("form" in elem || "label" in elem); - }, - { dir: "parentNode", next: "legend" } - ); - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var m, i, elem, nid, match, groups, newSelector, - newContext = context && context.ownerDocument, - - // nodeType defaults to 9, since context defaults to document - nodeType = context ? context.nodeType : 9; - - results = results || []; - - // Return early from calls with invalid selector or context - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - - return results; - } - - // Try to shortcut find operations (as opposed to filters) in HTML documents - if ( !seed ) { - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - context = context || document; - - if ( documentIsHTML ) { - - // If the selector is sufficiently simple, try using a "get*By*" DOM method - // (excepting DocumentFragment context, where the methods don't exist) - if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { - - // ID selector - if ( (m = match[1]) ) { - - // Document context - if ( nodeType === 9 ) { - if ( (elem = context.getElementById( m )) ) { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - - // Element context - } else { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( newContext && (elem = newContext.getElementById( m )) && - contains( context, elem ) && - elem.id === m ) { - - results.push( elem ); - return results; - } - } - - // Type selector - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Class selector - } else if ( (m = match[3]) && support.getElementsByClassName && - context.getElementsByClassName ) { - - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // Take advantage of querySelectorAll - if ( support.qsa && - !compilerCache[ selector + " " ] && - (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - - if ( nodeType !== 1 ) { - newContext = context; - newSelector = selector; - - // qSA looks outside Element context, which is not what we want - // Thanks to Andrew Dupont for this workaround technique - // Support: IE <=8 - // Exclude object elements - } else if ( context.nodeName.toLowerCase() !== "object" ) { - - // Capture the context ID, setting it first if necessary - if ( (nid = context.getAttribute( "id" )) ) { - nid = nid.replace( rcssescape, fcssescape ); - } else { - context.setAttribute( "id", (nid = expando) ); - } - - // Prefix every selector in the list - groups = tokenize( selector ); - i = groups.length; - while ( i-- ) { - groups[i] = "#" + nid + " " + toSelector( groups[i] ); - } - newSelector = groups.join( "," ); - - // Expand context for sibling selectors - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || - context; - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch ( qsaError ) { - } finally { - if ( nid === expando ) { - context.removeAttribute( "id" ); - } - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {function(string, object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created element and returns a boolean result - */ -function assert( fn ) { - var el = document.createElement("fieldset"); - - try { - return !!fn( el ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( el.parentNode ) { - el.parentNode.removeChild( el ); - } - // release memory in IE - el = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = arr.length; - - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - a.sourceIndex - b.sourceIndex; - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for :enabled/:disabled - * @param {Boolean} disabled true for :disabled; false for :enabled - */ -function createDisabledPseudo( disabled ) { - - // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable - return function( elem ) { - - // Only certain elements can match :enabled or :disabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled - if ( "form" in elem ) { - - // Check for inherited disabledness on relevant non-disabled elements: - // * listed form-associated elements in a disabled fieldset - // https://html.spec.whatwg.org/multipage/forms.html#category-listed - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled - // * option elements in a disabled optgroup - // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled - // All such elements have a "form" property. - if ( elem.parentNode && elem.disabled === false ) { - - // Option elements defer to a parent optgroup if present - if ( "label" in elem ) { - if ( "label" in elem.parentNode ) { - return elem.parentNode.disabled === disabled; - } else { - return elem.disabled === disabled; - } - } - - // Support: IE 6 - 11 - // Use the isDisabled shortcut property to check for disabled fieldset ancestors - return elem.isDisabled === disabled || - - // Where there is no isDisabled, check manually - /* jshint -W018 */ - elem.isDisabled !== !disabled && - disabledAncestor( elem ) === disabled; - } - - return elem.disabled === disabled; - - // Try to winnow out elements that can't be disabled before trusting the disabled property. - // Some victims get caught in our net (label, legend, menu, track), but it shouldn't - // even exist on them, let alone have a boolean value. - } else if ( "label" in elem ) { - return elem.disabled === disabled; - } - - // Remaining elements are neither :enabled nor :disabled - return false; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== "undefined" && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, subWindow, - doc = node ? node.ownerDocument || node : preferredDoc; - - // Return early if doc is invalid or already selected - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Update global variables - document = doc; - docElem = document.documentElement; - documentIsHTML = !isXML( document ); - - // Support: IE 9-11, Edge - // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) - if ( preferredDoc !== document && - (subWindow = document.defaultView) && subWindow.top !== subWindow ) { - - // Support: IE 11, Edge - if ( subWindow.addEventListener ) { - subWindow.addEventListener( "unload", unloadHandler, false ); - - // Support: IE 9 - 10 only - } else if ( subWindow.attachEvent ) { - subWindow.attachEvent( "onunload", unloadHandler ); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties - // (excepting IE8 booleans) - support.attributes = assert(function( el ) { - el.className = "i"; - return !el.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( el ) { - el.appendChild( document.createComment("") ); - return !el.getElementsByTagName("*").length; - }); - - // Support: IE<9 - support.getElementsByClassName = rnative.test( document.getElementsByClassName ); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programmatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( el ) { - docElem.appendChild( el ).id = expando; - return !document.getElementsByName || !document.getElementsByName( expando ).length; - }); - - // ID filter and find - if ( support.getById ) { - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var elem = context.getElementById( id ); - return elem ? [ elem ] : []; - } - }; - } else { - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && - elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - - // Support: IE 6 - 7 only - // getElementById is not reliable as a find shortcut - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var node, i, elems, - elem = context.getElementById( id ); - - if ( elem ) { - - // Verify the id attribute - node = elem.getAttributeNode("id"); - if ( node && node.value === id ) { - return [ elem ]; - } - - // Fall back on getElementsByName - elems = context.getElementsByName( id ); - i = 0; - while ( (elem = elems[i++]) ) { - node = elem.getAttributeNode("id"); - if ( node && node.value === id ) { - return [ elem ]; - } - } - } - - return []; - } - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else if ( support.qsa ) { - return context.querySelectorAll( tag ); - } - } : - - function( tag, context ) { - var elem, - tmp = [], - i = 0, - // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See https://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( el ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // https://bugs.jquery.com/ticket/12359 - docElem.appendChild( el ).innerHTML = "" + - ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( el.querySelectorAll("[msallowcapture^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !el.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ - if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push("~="); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !el.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - - // Support: Safari 8+, iOS 8+ - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibling-combinator selector` fails - if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push(".#.+[+~]"); - } - }); - - assert(function( el ) { - el.innerHTML = "" + - ""; - - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = document.createElement("input"); - input.setAttribute( "type", "hidden" ); - el.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( el.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( el.querySelectorAll(":enabled").length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: IE9-11+ - // IE's :disabled selector does not pick up the children of disabled fieldsets - docElem.appendChild( el ).disabled = true; - if ( el.querySelectorAll(":disabled").length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - el.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( el ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( el, "*" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( el, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully self-exclusive - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - return a === document ? -1 : - b === document ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return document; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - if ( support.matchesSelector && documentIsHTML && - !compilerCache[ expr + " " ] && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch (e) {} - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; -}; - -Sizzle.escape = function( sel ) { - return (sel + "").replace( rcssescape, fcssescape ); -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - while ( (node = elem[i++]) ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, uniqueCache, outerCache, node, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType, - diff = false; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) { - - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - - // Seek `elem` from a previously-cached index - - // ...in a gzip-friendly way - node = parent; - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex && cache[ 2 ]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - } else { - // Use previously-cached element index if available - if ( useCache ) { - // ...in a gzip-friendly way - node = elem; - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex; - } - - // xml :nth-child(...) - // or :nth-last-child(...) or :nth(-last)?-of-type(...) - if ( diff === false ) { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) && - ++diff ) { - - // Cache the index of each encountered element - if ( useCache ) { - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - uniqueCache[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - // Don't keep the element (issue #299) - input[0] = null; - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - text = text.replace( runescape, funescape ); - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": createDisabledPseudo( false ), - "disabled": createDisabledPseudo( true ), - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - skip = combinator.next, - key = skip || dir, - checkNonElements = base && key === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - return false; - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, uniqueCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); - - if ( skip && skip === elem.nodeName.toLowerCase() ) { - elem = elem[ dir ] || elem; - } else if ( (oldCache = uniqueCache[ key ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - // Reuse newcache so results back-propagate to previous elements - uniqueCache[ key ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - return false; - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - // Avoid hanging onto element (issue #299) - checkContext = null; - return ret; - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if ( outermost ) { - outermostContext = context === document || context || outermost; - } - - // Add elements passing elementMatchers directly to results - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - if ( !context && elem.ownerDocument !== document ) { - setDocument( elem ); - xml = !documentIsHTML; - } - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context || document, xml) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // `i` is now the count of elements visited above, and adding it to `matchedCount` - // makes the latter nonnegative. - matchedCount += i; - - // Apply set filters to unmatched elements - // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` - // equals `i`), unless we didn't visit _any_ elements in the above loop because we have - // no element matchers and no seed. - // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that - // case, which will result in a "00" `matchedCount` that differs from `i` but is also - // numerically zero. - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -}; - -/** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( (selector = compiled.selector || selector) ); - - results = results || []; - - // Try to minimize operations if there is only one selector in the list and no seed - // (the latter of which guarantees us context) - if ( match.length === 1 ) { - - // Reduce context if the leading compound selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { - - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - !context || rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - -// One-time assignments - -// Sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -// Support: Chrome 14-35+ -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - -// Initialize against the default document -setDocument(); - -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert(function( el ) { - // Should return 1, but returns 4 (following) - return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; -}); - -// Support: IE<8 -// Prevent attribute/property "interpolation" -// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert(function( el ) { - el.innerHTML = ""; - return el.firstChild.getAttribute("href") === "#" ; -}) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - }); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert(function( el ) { - el.innerHTML = ""; - el.firstChild.setAttribute( "value", "" ); - return el.firstChild.getAttribute( "value" ) === ""; -}) ) { - addHandle( "value", function( elem, name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - }); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert(function( el ) { - return el.getAttribute("disabled") == null; -}) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - null; - } - }); -} - -return Sizzle; - -})( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; - -// Deprecated -jQuery.expr[ ":" ] = jQuery.expr.pseudos; -jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; -jQuery.escapeSelector = Sizzle.escape; - - - - -var dir = function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; -}; - - -var siblings = function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; -}; - - -var rneedsContext = jQuery.expr.match.needsContext; - - - -function nodeName( elem, name ) { - - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - -}; -var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); - - - -var risSimple = /^.[^:#\[\.,]*$/; - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - return !!qualifier.call( elem, i, elem ) !== not; - } ); - } - - // Single element - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - } ); - } - - // Arraylike of elements (jQuery, arguments, Array) - if ( typeof qualifier !== "string" ) { - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) > -1 ) !== not; - } ); - } - - // Simple selector that can be filtered directly, removing non-Elements - if ( risSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - // Complex selector, compare the two sets, removing non-Elements - qualifier = jQuery.filter( qualifier, elements ); - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) > -1 ) !== not && elem.nodeType === 1; - } ); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - if ( elems.length === 1 && elem.nodeType === 1 ) { - return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; - } - - return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - } ) ); -}; - -jQuery.fn.extend( { - find: function( selector ) { - var i, ret, - len = this.length, - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter( function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - } ) ); - } - - ret = this.pushStack( [] ); - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - return len > 1 ? jQuery.uniqueSort( ret ) : ret; - }, - filter: function( selector ) { - return this.pushStack( winnow( this, selector || [], false ) ); - }, - not: function( selector ) { - return this.pushStack( winnow( this, selector || [], true ) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -} ); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - // Shortcut simple #id case for speed - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, - - init = jQuery.fn.init = function( selector, context, root ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Method init() accepts an alternate rootjQuery - // so migrate can support jQuery.sub (gh-2101) - root = root || rootjQuery; - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[ 0 ] === "<" && - selector[ selector.length - 1 ] === ">" && - selector.length >= 3 ) { - - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && ( match[ 1 ] || !context ) ) { - - // HANDLE: $(html) -> $(array) - if ( match[ 1 ] ) { - context = context instanceof jQuery ? context[ 0 ] : context; - - // Option to run scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[ 1 ], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[ 2 ] ); - - if ( elem ) { - - // Inject the element directly into the jQuery object - this[ 0 ] = elem; - this.length = 1; - } - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || root ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this[ 0 ] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return root.ready !== undefined ? - root.ready( selector ) : - - // Execute immediately if ready is not present - selector( jQuery ); - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - - // Methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend( { - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter( function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[ i ] ) ) { - return true; - } - } - } ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - targets = typeof selectors !== "string" && jQuery( selectors ); - - // Positional selectors never match, since there's no _selection_ context - if ( !rneedsContext.test( selectors ) ) { - for ( ; i < l; i++ ) { - for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { - - // Always skip document fragments - if ( cur.nodeType < 11 && ( targets ? - targets.index( cur ) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector( cur, selectors ) ) ) { - - matched.push( cur ); - break; - } - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); - }, - - // Determine the position of an element within the set - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // Index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.uniqueSort( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - } -} ); - -function sibling( cur, dir ) { - while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} - return cur; -} - -jQuery.each( { - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return siblings( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return siblings( elem.firstChild ); - }, - contents: function( elem ) { - if ( nodeName( elem, "iframe" ) ) { - return elem.contentDocument; - } - - // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only - // Treat the template element as a regular one in browsers that - // don't support it. - if ( nodeName( elem, "template" ) ) { - elem = elem.content || elem; - } - - return jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.uniqueSort( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -} ); -var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); - - - -// Convert String-formatted options into Object-formatted ones -function createOptions( options ) { - var object = {}; - jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { - object[ flag ] = true; - } ); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - createOptions( options ) : - jQuery.extend( {}, options ); - - var // Flag to know if list is currently firing - firing, - - // Last fire value for non-forgettable lists - memory, - - // Flag to know if list was already fired - fired, - - // Flag to prevent firing - locked, - - // Actual callback list - list = [], - - // Queue of execution data for repeatable lists - queue = [], - - // Index of currently firing callback (modified by add/remove as needed) - firingIndex = -1, - - // Fire callbacks - fire = function() { - - // Enforce single-firing - locked = locked || options.once; - - // Execute callbacks for all pending executions, - // respecting firingIndex overrides and runtime changes - fired = firing = true; - for ( ; queue.length; firingIndex = -1 ) { - memory = queue.shift(); - while ( ++firingIndex < list.length ) { - - // Run callback and check for early termination - if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && - options.stopOnFalse ) { - - // Jump to end and forget the data so .add doesn't re-fire - firingIndex = list.length; - memory = false; - } - } - } - - // Forget the data if we're done with it - if ( !options.memory ) { - memory = false; - } - - firing = false; - - // Clean up if we're done firing for good - if ( locked ) { - - // Keep an empty list if we have data for future add calls - if ( memory ) { - list = []; - - // Otherwise, this object is spent - } else { - list = ""; - } - } - }, - - // Actual Callbacks object - self = { - - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - - // If we have memory from a past run, we should fire after adding - if ( memory && !firing ) { - firingIndex = list.length - 1; - queue.push( memory ); - } - - ( function add( args ) { - jQuery.each( args, function( _, arg ) { - if ( jQuery.isFunction( arg ) ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { - - // Inspect recursively - add( arg ); - } - } ); - } )( arguments ); - - if ( memory && !firing ) { - fire(); - } - } - return this; - }, - - // Remove a callback from the list - remove: function() { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - - // Handle firing indexes - if ( index <= firingIndex ) { - firingIndex--; - } - } - } ); - return this; - }, - - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? - jQuery.inArray( fn, list ) > -1 : - list.length > 0; - }, - - // Remove all callbacks from the list - empty: function() { - if ( list ) { - list = []; - } - return this; - }, - - // Disable .fire and .add - // Abort any current/pending executions - // Clear all callbacks and values - disable: function() { - locked = queue = []; - list = memory = ""; - return this; - }, - disabled: function() { - return !list; - }, - - // Disable .fire - // Also disable .add unless we have memory (since it would have no effect) - // Abort any pending executions - lock: function() { - locked = queue = []; - if ( !memory && !firing ) { - list = memory = ""; - } - return this; - }, - locked: function() { - return !!locked; - }, - - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( !locked ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - queue.push( args ); - if ( !firing ) { - fire(); - } - } - return this; - }, - - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -function Identity( v ) { - return v; -} -function Thrower( ex ) { - throw ex; -} - -function adoptValue( value, resolve, reject, noValue ) { - var method; - - try { - - // Check for promise aspect first to privilege synchronous behavior - if ( value && jQuery.isFunction( ( method = value.promise ) ) ) { - method.call( value ).done( resolve ).fail( reject ); - - // Other thenables - } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) { - method.call( value, resolve, reject ); - - // Other non-thenables - } else { - - // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: - // * false: [ value ].slice( 0 ) => resolve( value ) - // * true: [ value ].slice( 1 ) => resolve() - resolve.apply( undefined, [ value ].slice( noValue ) ); - } - - // For Promises/A+, convert exceptions into rejections - // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in - // Deferred#then to conditionally suppress rejection. - } catch ( value ) { - - // Support: Android 4.0 only - // Strict mode functions invoked without .call/.apply get global-object context - reject.apply( undefined, [ value ] ); - } -} - -jQuery.extend( { - - Deferred: function( func ) { - var tuples = [ - - // action, add listener, callbacks, - // ... .then handlers, argument index, [final state] - [ "notify", "progress", jQuery.Callbacks( "memory" ), - jQuery.Callbacks( "memory" ), 2 ], - [ "resolve", "done", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 0, "resolved" ], - [ "reject", "fail", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 1, "rejected" ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - "catch": function( fn ) { - return promise.then( null, fn ); - }, - - // Keep pipe for back-compat - pipe: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - - return jQuery.Deferred( function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - - // Map tuples (progress, done, fail) to arguments (done, fail, progress) - var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; - - // deferred.progress(function() { bind to newDefer or newDefer.notify }) - // deferred.done(function() { bind to newDefer or newDefer.resolve }) - // deferred.fail(function() { bind to newDefer or newDefer.reject }) - deferred[ tuple[ 1 ] ]( function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .progress( newDefer.notify ) - .done( newDefer.resolve ) - .fail( newDefer.reject ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( - this, - fn ? [ returned ] : arguments - ); - } - } ); - } ); - fns = null; - } ).promise(); - }, - then: function( onFulfilled, onRejected, onProgress ) { - var maxDepth = 0; - function resolve( depth, deferred, handler, special ) { - return function() { - var that = this, - args = arguments, - mightThrow = function() { - var returned, then; - - // Support: Promises/A+ section 2.3.3.3.3 - // https://promisesaplus.com/#point-59 - // Ignore double-resolution attempts - if ( depth < maxDepth ) { - return; - } - - returned = handler.apply( that, args ); - - // Support: Promises/A+ section 2.3.1 - // https://promisesaplus.com/#point-48 - if ( returned === deferred.promise() ) { - throw new TypeError( "Thenable self-resolution" ); - } - - // Support: Promises/A+ sections 2.3.3.1, 3.5 - // https://promisesaplus.com/#point-54 - // https://promisesaplus.com/#point-75 - // Retrieve `then` only once - then = returned && - - // Support: Promises/A+ section 2.3.4 - // https://promisesaplus.com/#point-64 - // Only check objects and functions for thenability - ( typeof returned === "object" || - typeof returned === "function" ) && - returned.then; - - // Handle a returned thenable - if ( jQuery.isFunction( then ) ) { - - // Special processors (notify) just wait for resolution - if ( special ) { - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ) - ); - - // Normal processors (resolve) also hook into progress - } else { - - // ...and disregard older resolution values - maxDepth++; - - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ), - resolve( maxDepth, deferred, Identity, - deferred.notifyWith ) - ); - } - - // Handle all other returned values - } else { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Identity ) { - that = undefined; - args = [ returned ]; - } - - // Process the value(s) - // Default process is resolve - ( special || deferred.resolveWith )( that, args ); - } - }, - - // Only normal processors (resolve) catch and reject exceptions - process = special ? - mightThrow : - function() { - try { - mightThrow(); - } catch ( e ) { - - if ( jQuery.Deferred.exceptionHook ) { - jQuery.Deferred.exceptionHook( e, - process.stackTrace ); - } - - // Support: Promises/A+ section 2.3.3.3.4.1 - // https://promisesaplus.com/#point-61 - // Ignore post-resolution exceptions - if ( depth + 1 >= maxDepth ) { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Thrower ) { - that = undefined; - args = [ e ]; - } - - deferred.rejectWith( that, args ); - } - } - }; - - // Support: Promises/A+ section 2.3.3.3.1 - // https://promisesaplus.com/#point-57 - // Re-resolve promises immediately to dodge false rejection from - // subsequent errors - if ( depth ) { - process(); - } else { - - // Call an optional hook to record the stack, in case of exception - // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getStackHook ) { - process.stackTrace = jQuery.Deferred.getStackHook(); - } - window.setTimeout( process ); - } - }; - } - - return jQuery.Deferred( function( newDefer ) { - - // progress_handlers.add( ... ) - tuples[ 0 ][ 3 ].add( - resolve( - 0, - newDefer, - jQuery.isFunction( onProgress ) ? - onProgress : - Identity, - newDefer.notifyWith - ) - ); - - // fulfilled_handlers.add( ... ) - tuples[ 1 ][ 3 ].add( - resolve( - 0, - newDefer, - jQuery.isFunction( onFulfilled ) ? - onFulfilled : - Identity - ) - ); - - // rejected_handlers.add( ... ) - tuples[ 2 ][ 3 ].add( - resolve( - 0, - newDefer, - jQuery.isFunction( onRejected ) ? - onRejected : - Thrower - ) - ); - } ).promise(); - }, - - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 5 ]; - - // promise.progress = list.add - // promise.done = list.add - // promise.fail = list.add - promise[ tuple[ 1 ] ] = list.add; - - // Handle state - if ( stateString ) { - list.add( - function() { - - // state = "resolved" (i.e., fulfilled) - // state = "rejected" - state = stateString; - }, - - // rejected_callbacks.disable - // fulfilled_callbacks.disable - tuples[ 3 - i ][ 2 ].disable, - - // progress_callbacks.lock - tuples[ 0 ][ 2 ].lock - ); - } - - // progress_handlers.fire - // fulfilled_handlers.fire - // rejected_handlers.fire - list.add( tuple[ 3 ].fire ); - - // deferred.notify = function() { deferred.notifyWith(...) } - // deferred.resolve = function() { deferred.resolveWith(...) } - // deferred.reject = function() { deferred.rejectWith(...) } - deferred[ tuple[ 0 ] ] = function() { - deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); - return this; - }; - - // deferred.notifyWith = list.fireWith - // deferred.resolveWith = list.fireWith - // deferred.rejectWith = list.fireWith - deferred[ tuple[ 0 ] + "With" ] = list.fireWith; - } ); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( singleValue ) { - var - - // count of uncompleted subordinates - remaining = arguments.length, - - // count of unprocessed arguments - i = remaining, - - // subordinate fulfillment data - resolveContexts = Array( i ), - resolveValues = slice.call( arguments ), - - // the master Deferred - master = jQuery.Deferred(), - - // subordinate callback factory - updateFunc = function( i ) { - return function( value ) { - resolveContexts[ i ] = this; - resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( !( --remaining ) ) { - master.resolveWith( resolveContexts, resolveValues ); - } - }; - }; - - // Single- and empty arguments are adopted like Promise.resolve - if ( remaining <= 1 ) { - adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, - !remaining ); - - // Use .then() to unwrap secondary thenables (cf. gh-3000) - if ( master.state() === "pending" || - jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { - - return master.then(); - } - } - - // Multiple arguments are aggregated like Promise.all array elements - while ( i-- ) { - adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); - } - - return master.promise(); - } -} ); - - -// These usually indicate a programmer mistake during development, -// warn about them ASAP rather than swallowing them by default. -var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; - -jQuery.Deferred.exceptionHook = function( error, stack ) { - - // Support: IE 8 - 9 only - // Console exists when dev tools are open, which can happen at any time - if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { - window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); - } -}; - - - - -jQuery.readyException = function( error ) { - window.setTimeout( function() { - throw error; - } ); -}; - - - - -// The deferred used on DOM ready -var readyList = jQuery.Deferred(); - -jQuery.fn.ready = function( fn ) { - - readyList - .then( fn ) - - // Wrap jQuery.readyException in a function so that the lookup - // happens at the time of error handling instead of callback - // registration. - .catch( function( error ) { - jQuery.readyException( error ); - } ); - - return this; -}; - -jQuery.extend( { - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - } -} ); - -jQuery.ready.then = readyList.then; - -// The ready event handler and self cleanup method -function completed() { - document.removeEventListener( "DOMContentLoaded", completed ); - window.removeEventListener( "load", completed ); - jQuery.ready(); -} - -// Catch cases where $(document).ready() is called -// after the browser event has already occurred. -// Support: IE <=9 - 10 only -// Older IE sometimes signals "interactive" too soon -if ( document.readyState === "complete" || - ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - - // Handle it asynchronously to allow scripts the opportunity to delay ready - window.setTimeout( jQuery.ready ); - -} else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed ); -} - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - access( elems, fn, i, key[ i ], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( - elems[ i ], key, raw ? - value : - value.call( elems[ i ], i, fn( elems[ i ], key ) ) - ); - } - } - } - - if ( chainable ) { - return elems; - } - - // Gets - if ( bulk ) { - return fn.call( elems ); - } - - return len ? fn( elems[ 0 ], key ) : emptyGet; -}; -var acceptData = function( owner ) { - - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); -}; - - - - -function Data() { - this.expando = jQuery.expando + Data.uid++; -} - -Data.uid = 1; - -Data.prototype = { - - cache: function( owner ) { - - // Check if the owner object already has a cache - var value = owner[ this.expando ]; - - // If not, create one - if ( !value ) { - value = {}; - - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return an empty object. - if ( acceptData( owner ) ) { - - // If it is a node unlikely to be stringify-ed or looped over - // use plain assignment - if ( owner.nodeType ) { - owner[ this.expando ] = value; - - // Otherwise secure it in a non-enumerable property - // configurable must be true to allow the property to be - // deleted when data is removed - } else { - Object.defineProperty( owner, this.expando, { - value: value, - configurable: true - } ); - } - } - } - - return value; - }, - set: function( owner, data, value ) { - var prop, - cache = this.cache( owner ); - - // Handle: [ owner, key, value ] args - // Always use camelCase key (gh-2257) - if ( typeof data === "string" ) { - cache[ jQuery.camelCase( data ) ] = value; - - // Handle: [ owner, { properties } ] args - } else { - - // Copy the properties one-by-one to the cache object - for ( prop in data ) { - cache[ jQuery.camelCase( prop ) ] = data[ prop ]; - } - } - return cache; - }, - get: function( owner, key ) { - return key === undefined ? - this.cache( owner ) : - - // Always use camelCase key (gh-2257) - owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ]; - }, - access: function( owner, key, value ) { - - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ( ( key && typeof key === "string" ) && value === undefined ) ) { - - return this.get( owner, key ); - } - - // When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, - cache = owner[ this.expando ]; - - if ( cache === undefined ) { - return; - } - - if ( key !== undefined ) { - - // Support array or space separated string of keys - if ( Array.isArray( key ) ) { - - // If key is an array of keys... - // We always set camelCase keys, so remove that. - key = key.map( jQuery.camelCase ); - } else { - key = jQuery.camelCase( key ); - - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - key = key in cache ? - [ key ] : - ( key.match( rnothtmlwhite ) || [] ); - } - - i = key.length; - - while ( i-- ) { - delete cache[ key[ i ] ]; - } - } - - // Remove the expando if there's no more data - if ( key === undefined || jQuery.isEmptyObject( cache ) ) { - - // Support: Chrome <=35 - 45 - // Webkit & Blink performance suffers when deleting properties - // from DOM nodes, so set to undefined instead - // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) - if ( owner.nodeType ) { - owner[ this.expando ] = undefined; - } else { - delete owner[ this.expando ]; - } - } - }, - hasData: function( owner ) { - var cache = owner[ this.expando ]; - return cache !== undefined && !jQuery.isEmptyObject( cache ); - } -}; -var dataPriv = new Data(); - -var dataUser = new Data(); - - - -// Implementation Summary -// -// 1. Enforce API surface and semantic compatibility with 1.9.x branch -// 2. Improve the module's maintainability by reducing the storage -// paths to a single mechanism. -// 3. Use the same single mechanism to support "private" and "user" data. -// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) -// 5. Avoid exposing implementation details on user objects (eg. expando properties) -// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 - -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /[A-Z]/g; - -function getData( data ) { - if ( data === "true" ) { - return true; - } - - if ( data === "false" ) { - return false; - } - - if ( data === "null" ) { - return null; - } - - // Only convert to a number if it doesn't change the string - if ( data === +data + "" ) { - return +data; - } - - if ( rbrace.test( data ) ) { - return JSON.parse( data ); - } - - return data; -} - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = getData( data ); - } catch ( e ) {} - - // Make sure we set the data so it isn't changed later - dataUser.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} - -jQuery.extend( { - hasData: function( elem ) { - return dataUser.hasData( elem ) || dataPriv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return dataUser.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - dataUser.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to dataPriv methods, these can be deprecated. - _data: function( elem, name, data ) { - return dataPriv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - dataPriv.remove( elem, name ); - } -} ); - -jQuery.fn.extend( { - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = dataUser.get( elem ); - - if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE 11 only - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice( 5 ) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - dataPriv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each( function() { - dataUser.set( this, key ); - } ); - } - - return access( this, function( value ) { - var data; - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - - // Attempt to get data from the cache - // The key will always be camelCased in Data - data = dataUser.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, key ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each( function() { - - // We always store the camelCased key - dataUser.set( this, key, value ); - } ); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each( function() { - dataUser.remove( this, key ); - } ); - } -} ); - - -jQuery.extend( { - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = dataPriv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || Array.isArray( data ) ) { - queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // Clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // Not public - generate a queueHooks object, or return the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { - empty: jQuery.Callbacks( "once memory" ).add( function() { - dataPriv.remove( elem, [ type + "queue", key ] ); - } ) - } ); - } -} ); - -jQuery.fn.extend( { - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[ 0 ], type ); - } - - return data === undefined ? - this : - this.each( function() { - var queue = jQuery.queue( this, type, data ); - - // Ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - } ); - }, - dequeue: function( type ) { - return this.each( function() { - jQuery.dequeue( this, type ); - } ); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -} ); -var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; - -var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); - - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var isHiddenWithinTree = function( elem, el ) { - - // isHiddenWithinTree might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - - // Inline style trumps all - return elem.style.display === "none" || - elem.style.display === "" && - - // Otherwise, check computed style - // Support: Firefox <=43 - 45 - // Disconnected elements can have computed display: none, so first confirm that elem is - // in the document. - jQuery.contains( elem.ownerDocument, elem ) && - - jQuery.css( elem, "display" ) === "none"; - }; - -var swap = function( elem, options, callback, args ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.apply( elem, args || [] ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; -}; - - - - -function adjustCSS( elem, prop, valueParts, tween ) { - var adjusted, - scale = 1, - maxIterations = 20, - currentValue = tween ? - function() { - return tween.cur(); - } : - function() { - return jQuery.css( elem, prop, "" ); - }, - initial = currentValue(), - unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), - - // Starting value computation is required for potential unit mismatches - initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && - rcssNum.exec( jQuery.css( elem, prop ) ); - - if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { - - // Trust units reported by jQuery.css - unit = unit || initialInUnit[ 3 ]; - - // Make sure we update the tween properties later on - valueParts = valueParts || []; - - // Iteratively approximate from a nonzero starting point - initialInUnit = +initial || 1; - - do { - - // If previous iteration zeroed out, double until we get *something*. - // Use string for doubling so we don't accidentally see scale as unchanged below - scale = scale || ".5"; - - // Adjust and apply - initialInUnit = initialInUnit / scale; - jQuery.style( elem, prop, initialInUnit + unit ); - - // Update scale, tolerating zero or NaN from tween.cur() - // Break the loop if scale is unchanged or perfect, or if we've just had enough. - } while ( - scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations - ); - } - - if ( valueParts ) { - initialInUnit = +initialInUnit || +initial || 0; - - // Apply relative offset (+=/-=) if specified - adjusted = valueParts[ 1 ] ? - initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : - +valueParts[ 2 ]; - if ( tween ) { - tween.unit = unit; - tween.start = initialInUnit; - tween.end = adjusted; - } - } - return adjusted; -} - - -var defaultDisplayMap = {}; - -function getDefaultDisplay( elem ) { - var temp, - doc = elem.ownerDocument, - nodeName = elem.nodeName, - display = defaultDisplayMap[ nodeName ]; - - if ( display ) { - return display; - } - - temp = doc.body.appendChild( doc.createElement( nodeName ) ); - display = jQuery.css( temp, "display" ); - - temp.parentNode.removeChild( temp ); - - if ( display === "none" ) { - display = "block"; - } - defaultDisplayMap[ nodeName ] = display; - - return display; -} - -function showHide( elements, show ) { - var display, elem, - values = [], - index = 0, - length = elements.length; - - // Determine new display value for elements that need to change - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - display = elem.style.display; - if ( show ) { - - // Since we force visibility upon cascade-hidden elements, an immediate (and slow) - // check is required in this first loop unless we have a nonempty display value (either - // inline or about-to-be-restored) - if ( display === "none" ) { - values[ index ] = dataPriv.get( elem, "display" ) || null; - if ( !values[ index ] ) { - elem.style.display = ""; - } - } - if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { - values[ index ] = getDefaultDisplay( elem ); - } - } else { - if ( display !== "none" ) { - values[ index ] = "none"; - - // Remember what we're overwriting - dataPriv.set( elem, "display", display ); - } - } - } - - // Set the display of the elements in a second loop to avoid constant reflow - for ( index = 0; index < length; index++ ) { - if ( values[ index ] != null ) { - elements[ index ].style.display = values[ index ]; - } - } - - return elements; -} - -jQuery.fn.extend( { - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - if ( typeof state === "boolean" ) { - return state ? this.show() : this.hide(); - } - - return this.each( function() { - if ( isHiddenWithinTree( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - } ); - } -} ); -var rcheckableType = ( /^(?:checkbox|radio)$/i ); - -var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); - -var rscriptType = ( /^$|\/(?:java|ecma)script/i ); - - - -// We have to close these tags to support XHTML (#13200) -var wrapMap = { - - // Support: IE <=9 only - option: [ 1, "" ], - - // XHTML parsers do not magically insert elements in the - // same way that tag soup parsers do. So we cannot shorten - // this by omitting or other required elements. - thead: [ 1, "", "
" ], - col: [ 2, "", "
" ], - tr: [ 2, "", "
" ], - td: [ 3, "", "
" ], - - _default: [ 0, "", "" ] -}; - -// Support: IE <=9 only -wrapMap.optgroup = wrapMap.option; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - - -function getAll( context, tag ) { - - // Support: IE <=9 - 11 only - // Use typeof to avoid zero-argument method invocation on host objects (#15151) - var ret; - - if ( typeof context.getElementsByTagName !== "undefined" ) { - ret = context.getElementsByTagName( tag || "*" ); - - } else if ( typeof context.querySelectorAll !== "undefined" ) { - ret = context.querySelectorAll( tag || "*" ); - - } else { - ret = []; - } - - if ( tag === undefined || tag && nodeName( context, tag ) ) { - return jQuery.merge( [ context ], ret ); - } - - return ret; -} - - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - dataPriv.set( - elems[ i ], - "globalEval", - !refElements || dataPriv.get( refElements[ i ], "globalEval" ) - ); - } -} - - -var rhtml = /<|&#?\w+;/; - -function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, contains, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (#12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( ( elem = nodes[ i++ ] ) ) { - - // Skip elements already in the context collection (trac-4087) - if ( selection && jQuery.inArray( elem, selection ) > -1 ) { - if ( ignored ) { - ignored.push( elem ); - } - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( ( elem = tmp[ j++ ] ) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; -} - - -( function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // Support: Android 4.0 - 4.3 only - // Check state lost if the name is set (#11217) - // Support: Windows Web Apps (WWA) - // `name` and `type` must use .setAttribute for WWA (#14901) - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Android <=4.1 only - // Older WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE <=11 only - // Make sure textarea (and checkbox) defaultValue is properly cloned - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; -} )(); -var documentElement = document.documentElement; - - - -var - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -// Support: IE <=9 only -// See #13393 for more info -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -function on( elem, types, selector, data, fn, one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - on( elem, type, selector, data, types[ type ], one ); - } - return elem; - } - - if ( data == null && fn == null ) { - - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return elem; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return elem.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - } ); -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.get( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Ensure that invalid selectors throw exceptions at attach time - // Evaluate against documentElement in case elem is a non-element node (e.g., document) - if ( selector ) { - jQuery.find.matchesSelector( documentElement, selector ); - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !( events = elemData.events ) ) { - events = elemData.events = {}; - } - if ( !( eventHandle = elemData.handle ) ) { - eventHandle = elemData.handle = function( e ) { - - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend( { - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join( "." ) - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !( handlers = events[ type ] ) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || - special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - - if ( !elemData || !( events = elemData.events ) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[ 2 ] && - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || - selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || - special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove data and the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - dataPriv.remove( elem, "handle events" ); - } - }, - - dispatch: function( nativeEvent ) { - - // Make a writable jQuery.Event from the native event object - var event = jQuery.event.fix( nativeEvent ); - - var i, j, ret, matched, handleObj, handlerQueue, - args = new Array( arguments.length ), - handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[ 0 ] = event; - - for ( i = 1; i < arguments.length; i++ ) { - args[ i ] = arguments[ i ]; - } - - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( ( handleObj = matched.handlers[ j++ ] ) && - !event.isImmediatePropagationStopped() ) { - - // Triggered event must either 1) have no namespace, or 2) have namespace(s) - // a subset or equal to those in the bound event (both can have no namespace). - if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || - handleObj.handler ).apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( ( event.result = ret ) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, handleObj, sel, matchedHandlers, matchedSelectors, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - if ( delegateCount && - - // Support: IE <=9 - // Black-hole SVG instance trees (trac-13180) - cur.nodeType && - - // Support: Firefox <=42 - // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) - // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click - // Support: IE 11 only - // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) - !( event.type === "click" && event.button >= 1 ) ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't check non-elements (#13208) - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { - matchedHandlers = []; - matchedSelectors = {}; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matchedSelectors[ sel ] === undefined ) { - matchedSelectors[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) > -1 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matchedSelectors[ sel ] ) { - matchedHandlers.push( handleObj ); - } - } - if ( matchedHandlers.length ) { - handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); - } - } - } - } - - // Add the remaining (directly-bound) handlers - cur = this; - if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); - } - - return handlerQueue; - }, - - addProp: function( name, hook ) { - Object.defineProperty( jQuery.Event.prototype, name, { - enumerable: true, - configurable: true, - - get: jQuery.isFunction( hook ) ? - function() { - if ( this.originalEvent ) { - return hook( this.originalEvent ); - } - } : - function() { - if ( this.originalEvent ) { - return this.originalEvent[ name ]; - } - }, - - set: function( value ) { - Object.defineProperty( this, name, { - enumerable: true, - configurable: true, - writable: true, - value: value - } ); - } - } ); - }, - - fix: function( originalEvent ) { - return originalEvent[ jQuery.expando ] ? - originalEvent : - new jQuery.Event( originalEvent ); - }, - - special: { - load: { - - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - focus: { - - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== safeActiveElement() && this.focus ) { - this.focus(); - return false; - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === safeActiveElement() && this.blur ) { - this.blur(); - return false; - } - }, - delegateType: "focusout" - }, - click: { - - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { - this.click(); - return false; - } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default: function( event ) { - return nodeName( event.target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - } -}; - -jQuery.removeEvent = function( elem, type, handle ) { - - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } -}; - -jQuery.Event = function( src, props ) { - - // Allow instantiation without the 'new' keyword - if ( !( this instanceof jQuery.Event ) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - - // Support: Android <=2.3 only - src.returnValue === false ? - returnTrue : - returnFalse; - - // Create target properties - // Support: Safari <=6 - 7 only - // Target should not be a text node (#504, #13143) - this.target = ( src.target && src.target.nodeType === 3 ) ? - src.target.parentNode : - src.target; - - this.currentTarget = src.currentTarget; - this.relatedTarget = src.relatedTarget; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - constructor: jQuery.Event, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - isSimulated: false, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && !this.isSimulated ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Includes all common event props including KeyEvent and MouseEvent specific props -jQuery.each( { - altKey: true, - bubbles: true, - cancelable: true, - changedTouches: true, - ctrlKey: true, - detail: true, - eventPhase: true, - metaKey: true, - pageX: true, - pageY: true, - shiftKey: true, - view: true, - "char": true, - charCode: true, - key: true, - keyCode: true, - button: true, - buttons: true, - clientX: true, - clientY: true, - offsetX: true, - offsetY: true, - pointerId: true, - pointerType: true, - screenX: true, - screenY: true, - targetTouches: true, - toElement: true, - touches: true, - - which: function( event ) { - var button = event.button; - - // Add which for key events - if ( event.which == null && rkeyEvent.test( event.type ) ) { - return event.charCode != null ? event.charCode : event.keyCode; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { - if ( button & 1 ) { - return 1; - } - - if ( button & 2 ) { - return 3; - } - - if ( button & 4 ) { - return 2; - } - - return 0; - } - - return event.which; - } -}, jQuery.event.addProp ); - -// Create mouseenter/leave events using mouseover/out and event-time checks -// so that event delegation works in jQuery. -// Do the same for pointerenter/pointerleave and pointerover/pointerout -// -// Support: Safari 7 only -// Safari sends mouseenter too often; see: -// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 -// for the description of the bug (it existed in older Chrome versions as well). -jQuery.each( { - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mouseenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -} ); - -jQuery.fn.extend( { - - on: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn ); - }, - one: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? - handleObj.origType + "." + handleObj.namespace : - handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each( function() { - jQuery.event.remove( this, types, fn, selector ); - } ); - } -} ); - - -var - - /* eslint-disable max-len */ - - // See https://github.com/eslint/eslint/issues/3229 - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, - - /* eslint-enable */ - - // Support: IE <=10 - 11, Edge 12 - 13 - // In IE/Edge using regex groups here causes severe slowdowns. - // See https://connect.microsoft.com/IE/feedback/details/1736512/ - rnoInnerhtml = /\s*$/g; - -// Prefer a tbody over its parent table for containing new rows -function manipulationTarget( elem, content ) { - if ( nodeName( elem, "table" ) && - nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - - return jQuery( ">tbody", elem )[ 0 ] || elem; - } - - return elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - - if ( match ) { - elem.type = match[ 1 ]; - } else { - elem.removeAttribute( "type" ); - } - - return elem; -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( dataPriv.hasData( src ) ) { - pdataOld = dataPriv.access( src ); - pdataCur = dataPriv.set( dest, pdataOld ); - events = pdataOld.events; - - if ( events ) { - delete pdataCur.handle; - pdataCur.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( dataUser.hasData( src ) ) { - udataOld = dataUser.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - dataUser.set( dest, udataCur ); - } -} - -// Fix IE bugs, see support tests -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -function domManip( collection, args, callback, ignored ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = collection.length, - iNoClone = l - 1, - value = args[ 0 ], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return collection.each( function( index ) { - var self = collection.eq( index ); - if ( isFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - domManip( self, args, callback, ignored ); - } ); - } - - if ( l ) { - fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - // Require either new content or an interest in ignored elements to invoke the callback - if ( first || ignored ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item - // instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( collection[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !dataPriv.access( node, "globalEval" ) && - jQuery.contains( doc, node ) ) { - - if ( node.src ) { - - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); - } - } else { - DOMEval( node.textContent.replace( rcleanScript, "" ), doc ); - } - } - } - } - } - } - - return collection; -} - -function remove( elem, selector, keepData ) { - var node, - nodes = selector ? jQuery.filter( selector, elem ) : elem, - i = 0; - - for ( ; ( node = nodes[ i ] ) != null; i++ ) { - if ( !keepData && node.nodeType === 1 ) { - jQuery.cleanData( getAll( node ) ); - } - - if ( node.parentNode ) { - if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { - setGlobalEval( getAll( node, "script" ) ); - } - node.parentNode.removeChild( node ); - } - } - - return elem; -} - -jQuery.extend( { - htmlPrefilter: function( html ) { - return html.replace( rxhtmlTag, "<$1>" ); - }, - - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = jQuery.contains( elem.ownerDocument, elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - cleanData: function( elems ) { - var data, elem, type, - special = jQuery.event.special, - i = 0; - - for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { - if ( acceptData( elem ) ) { - if ( ( data = elem[ dataPriv.expando ] ) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataPriv.expando ] = undefined; - } - if ( elem[ dataUser.expando ] ) { - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataUser.expando ] = undefined; - } - } - } - } -} ); - -jQuery.fn.extend( { - detach: function( selector ) { - return remove( this, selector, true ); - }, - - remove: function( selector ) { - return remove( this, selector ); - }, - - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each( function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - } ); - }, null, value, arguments.length ); - }, - - append: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - } ); - }, - - prepend: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - } ); - }, - - before: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - } ); - }, - - after: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - } ); - }, - - empty: function() { - var elem, - i = 0; - - for ( ; ( elem = this[ i ] ) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - } ); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = jQuery.htmlPrefilter( value ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch ( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var ignored = []; - - // Make the changes, replacing each non-ignored context element with the new content - return domManip( this, arguments, function( elem ) { - var parent = this.parentNode; - - if ( jQuery.inArray( this, ignored ) < 0 ) { - jQuery.cleanData( getAll( this ) ); - if ( parent ) { - parent.replaceChild( elem, this ); - } - } - - // Force callback invocation - }, ignored ); - } -} ); - -jQuery.each( { - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: Android <=4.0 only, PhantomJS 1 only - // .get() because push.apply(_, arraylike) throws on ancient WebKit - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -} ); -var rmargin = ( /^margin/ ); - -var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); - -var getStyles = function( elem ) { - - // Support: IE <=11 only, Firefox <=30 (#15098, #14150) - // IE throws on elements created in popups - // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - var view = elem.ownerDocument.defaultView; - - if ( !view || !view.opener ) { - view = window; - } - - return view.getComputedStyle( elem ); - }; - - - -( function() { - - // Executing both pixelPosition & boxSizingReliable tests require only one layout - // so they're executed at the same time to save the second computation. - function computeStyleTests() { - - // This is a singleton, we need to execute it only once - if ( !div ) { - return; - } - - div.style.cssText = - "box-sizing:border-box;" + - "position:relative;display:block;" + - "margin:auto;border:1px;padding:1px;" + - "top:1%;width:50%"; - div.innerHTML = ""; - documentElement.appendChild( container ); - - var divStyle = window.getComputedStyle( div ); - pixelPositionVal = divStyle.top !== "1%"; - - // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = divStyle.marginLeft === "2px"; - boxSizingReliableVal = divStyle.width === "4px"; - - // Support: Android 4.0 - 4.3 only - // Some styles come back with percentage values, even though they shouldn't - div.style.marginRight = "50%"; - pixelMarginRightVal = divStyle.marginRight === "4px"; - - documentElement.removeChild( container ); - - // Nullify the div so it wouldn't be stored in the memory and - // it will also be a sign that checks already performed - div = null; - } - - var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal, - container = document.createElement( "div" ), - div = document.createElement( "div" ); - - // Finish early in limited (non-browser) environments - if ( !div.style ) { - return; - } - - // Support: IE <=9 - 11 only - // Style of cloned element affects source element cloned (#8908) - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" + - "padding:0;margin-top:1px;position:absolute"; - container.appendChild( div ); - - jQuery.extend( support, { - pixelPosition: function() { - computeStyleTests(); - return pixelPositionVal; - }, - boxSizingReliable: function() { - computeStyleTests(); - return boxSizingReliableVal; - }, - pixelMarginRight: function() { - computeStyleTests(); - return pixelMarginRightVal; - }, - reliableMarginLeft: function() { - computeStyleTests(); - return reliableMarginLeftVal; - } - } ); -} )(); - - -function curCSS( elem, name, computed ) { - var width, minWidth, maxWidth, ret, - - // Support: Firefox 51+ - // Retrieving style before computed somehow - // fixes an issue with getting wrong values - // on detached elements - style = elem.style; - - computed = computed || getStyles( elem ); - - // getPropertyValue is needed for: - // .css('filter') (IE 9 only, #12537) - // .css('--customProperty) (#3144) - if ( computed ) { - ret = computed.getPropertyValue( name ) || computed[ name ]; - - if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Android Browser returns percentage for some values, - // but width seems to be reliably pixels. - // This is against the CSSOM draft spec: - // https://drafts.csswg.org/cssom/#resolved-values - if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret !== undefined ? - - // Support: IE <=9 - 11 only - // IE returns zIndex value as an integer. - ret + "" : - ret; -} - - -function addGetHookIf( conditionFn, hookFn ) { - - // Define the hook, we'll check on the first run if it's really needed. - return { - get: function() { - if ( conditionFn() ) { - - // Hook not needed (or it's not possible to use it due - // to missing dependency), remove it. - delete this.get; - return; - } - - // Hook needed; redefine it so that the support test is not executed again. - return ( this.get = hookFn ).apply( this, arguments ); - } - }; -} - - -var - - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rcustomProp = /^--/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }, - - cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style; - -// Return a css property mapped to a potentially vendor prefixed property -function vendorPropName( name ) { - - // Shortcut for names that are not vendor prefixed - if ( name in emptyStyle ) { - return name; - } - - // Check for vendor prefixed names - var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in emptyStyle ) { - return name; - } - } -} - -// Return a property mapped along what jQuery.cssProps suggests or to -// a vendor prefixed property. -function finalPropName( name ) { - var ret = jQuery.cssProps[ name ]; - if ( !ret ) { - ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; - } - return ret; -} - -function setPositiveNumber( elem, value, subtract ) { - - // Any relative (+/-) values have already been - // normalized at this point - var matches = rcssNum.exec( value ); - return matches ? - - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : - value; -} - -function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { - var i, - val = 0; - - // If we already have the right measurement, avoid augmentation - if ( extra === ( isBorderBox ? "border" : "content" ) ) { - i = 4; - - // Otherwise initialize for horizontal or vertical properties - } else { - i = name === "width" ? 1 : 0; - } - - for ( ; i < 4; i += 2 ) { - - // Both box models exclude margin, so add it if we want it - if ( extra === "margin" ) { - val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); - } - - if ( isBorderBox ) { - - // border-box includes padding, so remove it if we want content - if ( extra === "content" ) { - val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // At this point, extra isn't border nor margin, so remove border - if ( extra !== "margin" ) { - val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } else { - - // At this point, extra isn't content, so add padding - val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // At this point, extra isn't content nor padding, so add border - if ( extra !== "padding" ) { - val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - return val; -} - -function getWidthOrHeight( elem, name, extra ) { - - // Start with computed style - var valueIsBorderBox, - styles = getStyles( elem ), - val = curCSS( elem, name, styles ), - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // Computed unit is not pixels. Stop here and return. - if ( rnumnonpx.test( val ) ) { - return val; - } - - // Check for style in case a browser which returns unreliable values - // for getComputedStyle silently falls back to the reliable elem.style - valueIsBorderBox = isBorderBox && - ( support.boxSizingReliable() || val === elem.style[ name ] ); - - // Fall back to offsetWidth/Height when value is "auto" - // This happens for inline elements with no explicit setting (gh-3571) - if ( val === "auto" ) { - val = elem[ "offset" + name[ 0 ].toUpperCase() + name.slice( 1 ) ]; - } - - // Normalize "", auto, and prepare for extra - val = parseFloat( val ) || 0; - - // Use the active box-sizing model to add/subtract irrelevant styles - return ( val + - augmentWidthOrHeight( - elem, - name, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles - ) - ) + "px"; -} - -jQuery.extend( { - - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - "animationIterationCount": true, - "columnCount": true, - "fillOpacity": true, - "flexGrow": true, - "flexShrink": true, - "fontWeight": true, - "lineHeight": true, - "opacity": true, - "order": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: { - "float": "cssFloat" - }, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = jQuery.camelCase( name ), - isCustomProp = rcustomProp.test( name ), - style = elem.style; - - // Make sure that we're working with the right name. We don't - // want to query the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Gets hook for the prefixed version, then unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // Convert "+=" or "-=" to relative numbers (#7345) - if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { - value = adjustCSS( elem, name, ret ); - - // Fixes bug #9237 - type = "number"; - } - - // Make sure that null and NaN values aren't set (#7116) - if ( value == null || value !== value ) { - return; - } - - // If a number was passed in, add the unit (except for certain CSS properties) - if ( type === "number" ) { - value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); - } - - // background-* props affect original clone's values - if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !( "set" in hooks ) || - ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - - if ( isCustomProp ) { - style.setProperty( name, value ); - } else { - style[ name ] = value; - } - } - - } else { - - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && - ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { - - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = jQuery.camelCase( name ), - isCustomProp = rcustomProp.test( name ); - - // Make sure that we're working with the right name. We don't - // want to modify the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Try prefixed name followed by the unprefixed name - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - // Convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Make numeric if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || isFinite( num ) ? num || 0 : val; - } - - return val; - } -} ); - -jQuery.each( [ "height", "width" ], function( i, name ) { - jQuery.cssHooks[ name ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - - // Certain elements can have dimension info if we invisibly show them - // but it must have a current display style that would benefit - return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - - // Support: Safari 8+ - // Table columns in Safari have non-zero offsetWidth & zero - // getBoundingClientRect().width unless display is changed. - // Support: IE <=11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, name, extra ); - } ) : - getWidthOrHeight( elem, name, extra ); - } - }, - - set: function( elem, value, extra ) { - var matches, - styles = extra && getStyles( elem ), - subtract = extra && augmentWidthOrHeight( - elem, - name, - extra, - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - styles - ); - - // Convert to pixels if value adjustment is needed - if ( subtract && ( matches = rcssNum.exec( value ) ) && - ( matches[ 3 ] || "px" ) !== "px" ) { - - elem.style[ name ] = value; - value = jQuery.css( elem, name ); - } - - return setPositiveNumber( elem, value, subtract ); - } - }; -} ); - -jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, - function( elem, computed ) { - if ( computed ) { - return ( parseFloat( curCSS( elem, "marginLeft" ) ) || - elem.getBoundingClientRect().left - - swap( elem, { marginLeft: 0 }, function() { - return elem.getBoundingClientRect().left; - } ) - ) + "px"; - } - } -); - -// These hooks are used by animate to expand properties -jQuery.each( { - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i = 0, - expanded = {}, - - // Assumes a single number if not a string - parts = typeof value === "string" ? value.split( " " ) : [ value ]; - - for ( ; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( !rmargin.test( prefix ) ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -} ); - -jQuery.fn.extend( { - css: function( name, value ) { - return access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( Array.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - } -} ); - - -function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); -} -jQuery.Tween = Tween; - -Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || jQuery.easing._default; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } -}; - -Tween.prototype.init.prototype = Tween.prototype; - -Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - // Use a property on the element directly when it is not a DOM element, - // or when there is no matching style property that exists. - if ( tween.elem.nodeType !== 1 || - tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { - return tween.elem[ tween.prop ]; - } - - // Passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails. - // Simple values such as "10px" are parsed to Float; - // complex values such as "rotate(1rad)" are returned as-is. - result = jQuery.css( tween.elem, tween.prop, "" ); - - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - - // Use step hook for back compat. - // Use cssHook if its there. - // Use .style if available and use plain properties where available. - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && - ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || - jQuery.cssHooks[ tween.prop ] ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } -}; - -// Support: IE <=9 only -// Panic based approach to setting things on disconnected nodes -Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p * Math.PI ) / 2; - }, - _default: "swing" -}; - -jQuery.fx = Tween.prototype.init; - -// Back compat <1.8 extension point -jQuery.fx.step = {}; - - - - -var - fxNow, inProgress, - rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/; - -function schedule() { - if ( inProgress ) { - if ( document.hidden === false && window.requestAnimationFrame ) { - window.requestAnimationFrame( schedule ); - } else { - window.setTimeout( schedule, jQuery.fx.interval ); - } - - jQuery.fx.tick(); - } -} - -// Animations created synchronously will run synchronously -function createFxNow() { - window.setTimeout( function() { - fxNow = undefined; - } ); - return ( fxNow = jQuery.now() ); -} - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - i = 0, - attrs = { height: type }; - - // If we include width, step value is 1 to do all cssExpand values, - // otherwise step value is 2 to skip over Left and Right - includeWidth = includeWidth ? 1 : 0; - for ( ; i < 4; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; -} - -function createTween( value, prop, animation ) { - var tween, - collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { - - // We're done with this property - return tween; - } - } -} - -function defaultPrefilter( elem, props, opts ) { - var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, - isBox = "width" in props || "height" in props, - anim = this, - orig = {}, - style = elem.style, - hidden = elem.nodeType && isHiddenWithinTree( elem ), - dataShow = dataPriv.get( elem, "fxshow" ); - - // Queue-skipping animations hijack the fx hooks - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always( function() { - - // Ensure the complete handler is called before this completes - anim.always( function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - } ); - } ); - } - - // Detect show/hide animations - for ( prop in props ) { - value = props[ prop ]; - if ( rfxtypes.test( value ) ) { - delete props[ prop ]; - toggle = toggle || value === "toggle"; - if ( value === ( hidden ? "hide" : "show" ) ) { - - // Pretend to be hidden if this is a "show" and - // there is still data from a stopped show/hide - if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { - hidden = true; - - // Ignore all other no-op show/hide data - } else { - continue; - } - } - orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); - } - } - - // Bail out if this is a no-op like .hide().hide() - propTween = !jQuery.isEmptyObject( props ); - if ( !propTween && jQuery.isEmptyObject( orig ) ) { - return; - } - - // Restrict "overflow" and "display" styles during box animations - if ( isBox && elem.nodeType === 1 ) { - - // Support: IE <=9 - 11, Edge 12 - 13 - // Record all 3 overflow attributes because IE does not infer the shorthand - // from identically-valued overflowX and overflowY - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Identify a display type, preferring old show/hide data over the CSS cascade - restoreDisplay = dataShow && dataShow.display; - if ( restoreDisplay == null ) { - restoreDisplay = dataPriv.get( elem, "display" ); - } - display = jQuery.css( elem, "display" ); - if ( display === "none" ) { - if ( restoreDisplay ) { - display = restoreDisplay; - } else { - - // Get nonempty value(s) by temporarily forcing visibility - showHide( [ elem ], true ); - restoreDisplay = elem.style.display || restoreDisplay; - display = jQuery.css( elem, "display" ); - showHide( [ elem ] ); - } - } - - // Animate inline elements as inline-block - if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { - if ( jQuery.css( elem, "float" ) === "none" ) { - - // Restore the original display value at the end of pure show/hide animations - if ( !propTween ) { - anim.done( function() { - style.display = restoreDisplay; - } ); - if ( restoreDisplay == null ) { - display = style.display; - restoreDisplay = display === "none" ? "" : display; - } - } - style.display = "inline-block"; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - anim.always( function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - } ); - } - - // Implement show/hide animations - propTween = false; - for ( prop in orig ) { - - // General show/hide setup for this element animation - if ( !propTween ) { - if ( dataShow ) { - if ( "hidden" in dataShow ) { - hidden = dataShow.hidden; - } - } else { - dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); - } - - // Store hidden/visible for toggle so `.stop().toggle()` "reverses" - if ( toggle ) { - dataShow.hidden = !hidden; - } - - // Show elements before animating them - if ( hidden ) { - showHide( [ elem ], true ); - } - - /* eslint-disable no-loop-func */ - - anim.done( function() { - - /* eslint-enable no-loop-func */ - - // The final step of a "hide" animation is actually hiding the element - if ( !hidden ) { - showHide( [ elem ] ); - } - dataPriv.remove( elem, "fxshow" ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - } ); - } - - // Per-property setup - propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = propTween.start; - if ( hidden ) { - propTween.end = propTween.start; - propTween.start = 0; - } - } - } -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = jQuery.camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( Array.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // Not quite $.extend, this won't overwrite existing keys. - // Reusing 'index' because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } -} - -function Animation( elem, properties, options ) { - var result, - stopped, - index = 0, - length = Animation.prefilters.length, - deferred = jQuery.Deferred().always( function() { - - // Don't match elem in the :animated selector - delete tick.elem; - } ), - tick = function() { - if ( stopped ) { - return false; - } - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - - // Support: Android 2.3 only - // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) - temp = remaining / animation.duration || 0, - percent = 1 - temp, - index = 0, - length = animation.tweens.length; - - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ] ); - - // If there's more to do, yield - if ( percent < 1 && length ) { - return remaining; - } - - // If this was an empty animation, synthesize a final progress notification - if ( !length ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - } - - // Resolve the animation and report its conclusion - deferred.resolveWith( elem, [ animation ] ); - return false; - }, - animation = deferred.promise( { - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { - specialEasing: {}, - easing: jQuery.easing._default - }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - - // If we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - if ( stopped ) { - return this; - } - stopped = true; - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // Resolve when we played the last frame; otherwise, reject - if ( gotoEnd ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - } ), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length; index++ ) { - result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - if ( jQuery.isFunction( result.stop ) ) { - jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - jQuery.proxy( result.stop, result ); - } - return result; - } - } - - jQuery.map( props, createTween, animation ); - - if ( jQuery.isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - // Attach callbacks from options - animation - .progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); - - jQuery.fx.timer( - jQuery.extend( tick, { - elem: elem, - anim: animation, - queue: animation.opts.queue - } ) - ); - - return animation; -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweeners: { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }, - - tweener: function( props, callback ) { - if ( jQuery.isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.match( rnothtmlwhite ); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length; index++ ) { - prop = props[ index ]; - Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; - Animation.tweeners[ prop ].unshift( callback ); - } - }, - - prefilters: [ defaultPrefilter ], - - prefilter: function( callback, prepend ) { - if ( prepend ) { - Animation.prefilters.unshift( callback ); - } else { - Animation.prefilters.push( callback ); - } - } -} ); - -jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - jQuery.isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing - }; - - // Go to the end state if fx are off - if ( jQuery.fx.off ) { - opt.duration = 0; - - } else { - if ( typeof opt.duration !== "number" ) { - if ( opt.duration in jQuery.fx.speeds ) { - opt.duration = jQuery.fx.speeds[ opt.duration ]; - - } else { - opt.duration = jQuery.fx.speeds._default; - } - } - } - - // Normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( jQuery.isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.fn.extend( { - fadeTo: function( speed, to, easing, callback ) { - - // Show any hidden elements after setting opacity to 0 - return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() - - // Animate to the value specified - .end().animate( { opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations, or finishing resolves immediately - if ( empty || dataPriv.get( this, "finish" ) ) { - anim.stop( true ); - } - }; - doAnimation.finish = doAnimation; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue && type !== false ) { - this.queue( type || "fx", [] ); - } - - return this.each( function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = dataPriv.get( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && - ( type == null || timers[ index ].queue === type ) ) { - - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // Start the next in the queue if the last step wasn't forced. - // Timers currently will call their complete callbacks, which - // will dequeue but only if they were gotoEnd. - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - } ); - }, - finish: function( type ) { - if ( type !== false ) { - type = type || "fx"; - } - return this.each( function() { - var index, - data = dataPriv.get( this ), - queue = data[ type + "queue" ], - hooks = data[ type + "queueHooks" ], - timers = jQuery.timers, - length = queue ? queue.length : 0; - - // Enable finishing flag on private data - data.finish = true; - - // Empty the queue first - jQuery.queue( this, type, [] ); - - if ( hooks && hooks.stop ) { - hooks.stop.call( this, true ); - } - - // Look for any active animations, and finish them - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && timers[ index ].queue === type ) { - timers[ index ].anim.stop( true ); - timers.splice( index, 1 ); - } - } - - // Look for any animations in the old queue and finish them - for ( index = 0; index < length; index++ ) { - if ( queue[ index ] && queue[ index ].finish ) { - queue[ index ].finish.call( this ); - } - } - - // Turn off finishing flag - delete data.finish; - } ); - } -} ); - -jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -} ); - -// Generate shortcuts for custom animations -jQuery.each( { - slideDown: genFx( "show" ), - slideUp: genFx( "hide" ), - slideToggle: genFx( "toggle" ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -} ); - -jQuery.timers = []; -jQuery.fx.tick = function() { - var timer, - i = 0, - timers = jQuery.timers; - - fxNow = jQuery.now(); - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - - // Run the timer and safely remove it when done (allowing for external removal) - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - fxNow = undefined; -}; - -jQuery.fx.timer = function( timer ) { - jQuery.timers.push( timer ); - jQuery.fx.start(); -}; - -jQuery.fx.interval = 13; -jQuery.fx.start = function() { - if ( inProgress ) { - return; - } - - inProgress = true; - schedule(); -}; - -jQuery.fx.stop = function() { - inProgress = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - - // Default speed - _default: 400 -}; - - -// Based off of the plugin by Clint Helfers, with permission. -// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ -jQuery.fn.delay = function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = window.setTimeout( next, time ); - hooks.stop = function() { - window.clearTimeout( timeout ); - }; - } ); -}; - - -( function() { - var input = document.createElement( "input" ), - select = document.createElement( "select" ), - opt = select.appendChild( document.createElement( "option" ) ); - - input.type = "checkbox"; - - // Support: Android <=4.3 only - // Default value for a checkbox should be "on" - support.checkOn = input.value !== ""; - - // Support: IE <=11 only - // Must access selectedIndex to make default options select - support.optSelected = opt.selected; - - // Support: IE <=11 only - // An input loses its value after becoming a radio - input = document.createElement( "input" ); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; -} )(); - - -var boolHook, - attrHandle = jQuery.expr.attrHandle; - -jQuery.fn.extend( { - attr: function( name, value ) { - return access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each( function() { - jQuery.removeAttr( this, name ); - } ); - } -} ); - -jQuery.extend( { - attr: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set attributes on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - // Attribute hooks are determined by the lowercase version - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - hooks = jQuery.attrHooks[ name.toLowerCase() ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); - } - - if ( value !== undefined ) { - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - } - - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - elem.setAttribute( name, value + "" ); - return value; - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? undefined : ret; - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - removeAttr: function( elem, value ) { - var name, - i = 0, - - // Attribute names can contain non-HTML whitespace characters - // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 - attrNames = value && value.match( rnothtmlwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( ( name = attrNames[ i++ ] ) ) { - elem.removeAttribute( name ); - } - } - } -} ); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; - -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { - var getter = attrHandle[ name ] || jQuery.find.attr; - - attrHandle[ name ] = function( elem, name, isXML ) { - var ret, handle, - lowercaseName = name.toLowerCase(); - - if ( !isXML ) { - - // Avoid an infinite loop by temporarily removing this function from the getter - handle = attrHandle[ lowercaseName ]; - attrHandle[ lowercaseName ] = ret; - ret = getter( elem, name, isXML ) != null ? - lowercaseName : - null; - attrHandle[ lowercaseName ] = handle; - } - return ret; - }; -} ); - - - - -var rfocusable = /^(?:input|select|textarea|button)$/i, - rclickable = /^(?:a|area)$/i; - -jQuery.fn.extend( { - prop: function( name, value ) { - return access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each( function() { - delete this[ jQuery.propFix[ name ] || name ]; - } ); - } -} ); - -jQuery.extend( { - prop: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set properties on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - return ( elem[ name ] = value ); - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - return elem[ name ]; - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - - // Support: IE <=9 - 11 only - // elem.tabIndex doesn't always return the - // correct value when it hasn't been explicitly set - // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - // Use proper attribute retrieval(#12072) - var tabindex = jQuery.find.attr( elem, "tabindex" ); - - if ( tabindex ) { - return parseInt( tabindex, 10 ); - } - - if ( - rfocusable.test( elem.nodeName ) || - rclickable.test( elem.nodeName ) && - elem.href - ) { - return 0; - } - - return -1; - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - } -} ); - -// Support: IE <=11 only -// Accessing the selectedIndex property -// forces the browser to respect setting selected -// on the option -// The getter ensures a default option is selected -// when in an optgroup -// eslint rule "no-unused-expressions" is disabled for this code -// since it considers such accessions noop -if ( !support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - }, - set: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - }; -} - -jQuery.each( [ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -} ); - - - - - // Strip and collapse whitespace according to HTML spec - // https://html.spec.whatwg.org/multipage/infrastructure.html#strip-and-collapse-whitespace - function stripAndCollapse( value ) { - var tokens = value.match( rnothtmlwhite ) || []; - return tokens.join( " " ); - } - - -function getClass( elem ) { - return elem.getAttribute && elem.getAttribute( "class" ) || ""; -} - -jQuery.fn.extend( { - addClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( jQuery.isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( typeof value === "string" && value ) { - classes = value.match( rnothtmlwhite ) || []; - - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( jQuery.isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( !arguments.length ) { - return this.attr( "class", "" ); - } - - if ( typeof value === "string" && value ) { - classes = value.match( rnothtmlwhite ) || []; - - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) > -1 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value; - - if ( typeof stateVal === "boolean" && type === "string" ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - if ( jQuery.isFunction( value ) ) { - return this.each( function( i ) { - jQuery( this ).toggleClass( - value.call( this, i, getClass( this ), stateVal ), - stateVal - ); - } ); - } - - return this.each( function() { - var className, i, self, classNames; - - if ( type === "string" ) { - - // Toggle individual class names - i = 0; - self = jQuery( this ); - classNames = value.match( rnothtmlwhite ) || []; - - while ( ( className = classNames[ i++ ] ) ) { - - // Check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( value === undefined || type === "boolean" ) { - className = getClass( this ); - if ( className ) { - - // Store className if set - dataPriv.set( this, "__className__", className ); - } - - // If the element has a class name or if we're passed `false`, - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - if ( this.setAttribute ) { - this.setAttribute( "class", - className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" - ); - } - } - } ); - }, - - hasClass: function( selector ) { - var className, elem, - i = 0; - - className = " " + selector + " "; - while ( ( elem = this[ i++ ] ) ) { - if ( elem.nodeType === 1 && - ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; - } - } - - return false; - } -} ); - - - - -var rreturn = /\r/g; - -jQuery.fn.extend( { - val: function( value ) { - var hooks, ret, isFunction, - elem = this[ 0 ]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || - jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && - "get" in hooks && - ( ret = hooks.get( elem, "value" ) ) !== undefined - ) { - return ret; - } - - ret = elem.value; - - // Handle most common string cases - if ( typeof ret === "string" ) { - return ret.replace( rreturn, "" ); - } - - // Handle cases where value is null/undef or number - return ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each( function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - - } else if ( typeof val === "number" ) { - val += ""; - - } else if ( Array.isArray( val ) ) { - val = jQuery.map( val, function( value ) { - return value == null ? "" : value + ""; - } ); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - } ); - } -} ); - -jQuery.extend( { - valHooks: { - option: { - get: function( elem ) { - - var val = jQuery.find.attr( elem, "value" ); - return val != null ? - val : - - // Support: IE <=10 - 11 only - // option.text throws exceptions (#14686, #14858) - // Strip and collapse whitespace - // https://html.spec.whatwg.org/#strip-and-collapse-whitespace - stripAndCollapse( jQuery.text( elem ) ); - } - }, - select: { - get: function( elem ) { - var value, option, i, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one", - values = one ? null : [], - max = one ? index + 1 : options.length; - - if ( index < 0 ) { - i = max; - - } else { - i = one ? index : 0; - } - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Support: IE <=9 only - // IE8-9 doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - - // Don't return options that are disabled or in a disabled optgroup - !option.disabled && - ( !option.parentNode.disabled || - !nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - - /* eslint-disable no-cond-assign */ - - if ( option.selected = - jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 - ) { - optionSet = true; - } - - /* eslint-enable no-cond-assign */ - } - - // Force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - } -} ); - -// Radios and checkboxes getter/setter -jQuery.each( [ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( Array.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); - } - } - }; - if ( !support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - return elem.getAttribute( "value" ) === null ? "on" : elem.value; - }; - } -} ); - - - - -// Return jQuery for attributes-only inclusion - - -var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/; - -jQuery.extend( jQuery.event, { - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && - dataPriv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( ( !special._default || - special._default.apply( eventPath.pop(), data ) === false ) && - acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - // Piggyback on a donor event to simulate a different one - // Used only for `focus(in | out)` events - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - } - ); - - jQuery.event.trigger( e, null, elem ); - } - -} ); - -jQuery.fn.extend( { - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -} ); - - -jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup contextmenu" ).split( " " ), - function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( data, fn ) { - return arguments.length > 0 ? - this.on( name, null, data, fn ) : - this.trigger( name ); - }; -} ); - -jQuery.fn.extend( { - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -} ); - - - - -support.focusin = "onfocusin" in window; - - -// Support: Firefox <=44 -// Firefox doesn't have focus(in | out) events -// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 -// -// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 -// focus(in | out) events fire after focus & blur events, -// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order -// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 -if ( !support.focusin ) { - jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = dataPriv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = dataPriv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - dataPriv.remove( doc, fix ); - - } else { - dataPriv.access( doc, fix, attaches ); - } - } - }; - } ); -} -var location = window.location; - -var nonce = jQuery.now(); - -var rquery = ( /\?/ ); - - - -// Cross-browser xml parsing -jQuery.parseXML = function( data ) { - var xml; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) { - xml = undefined; - } - - if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; -}; - - -var - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, - rsubmittable = /^(?:input|select|textarea|keygen)/i; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( Array.isArray( obj ) ) { - - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - - // Item is non-scalar (array or object), encode its numeric index. - buildParams( - prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", - v, - traditional, - add - ); - } - } ); - - } else if ( !traditional && jQuery.type( obj ) === "object" ) { - - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - - // Serialize scalar item. - add( prefix, obj ); - } -} - -// Serialize an array of form elements or a set of -// key/values into a query string -jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, valueOrFunction ) { - - // If value is a function, invoke it and use its return value - var value = jQuery.isFunction( valueOrFunction ) ? - valueOrFunction() : - valueOrFunction; - - s[ s.length ] = encodeURIComponent( key ) + "=" + - encodeURIComponent( value == null ? "" : value ); - }; - - // If an array was passed in, assume that it is an array of form elements. - if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - } ); - - } else { - - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ); -}; - -jQuery.fn.extend( { - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map( function() { - - // Can add propHook for "elements" to filter or add form elements - var elements = jQuery.prop( this, "elements" ); - return elements ? jQuery.makeArray( elements ) : this; - } ) - .filter( function() { - var type = this.type; - - // Use .is( ":disabled" ) so that fieldset[disabled] works - return this.name && !jQuery( this ).is( ":disabled" ) && - rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckableType.test( type ) ); - } ) - .map( function( i, elem ) { - var val = jQuery( this ).val(); - - if ( val == null ) { - return null; - } - - if ( Array.isArray( val ) ) { - return jQuery.map( val, function( val ) { - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ); - } - - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ).get(); - } -} ); - - -var - r20 = /%20/g, - rhash = /#.*$/, - rantiCache = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, - - // #7653, #8125, #8152: local protocol detection - rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression - allTypes = "*/".concat( "*" ), - - // Anchor tag for parsing the document origin - originAnchor = document.createElement( "a" ); - originAnchor.href = location.href; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, - i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; - - if ( jQuery.isFunction( func ) ) { - - // For each dataType in the dataTypeExpression - while ( ( dataType = dataTypes[ i++ ] ) ) { - - // Prepend if requested - if ( dataType[ 0 ] === "+" ) { - dataType = dataType.slice( 1 ) || "*"; - ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); - - // Otherwise append - } else { - ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); - } - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { - - var inspected = {}, - seekingTransport = ( structure === transports ); - - function inspect( dataType ) { - var selected; - inspected[ dataType ] = true; - jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { - var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if ( typeof dataTypeOrTransport === "string" && - !seekingTransport && !inspected[ dataTypeOrTransport ] ) { - - options.dataTypes.unshift( dataTypeOrTransport ); - inspect( dataTypeOrTransport ); - return false; - } else if ( seekingTransport ) { - return !( selected = dataTypeOrTransport ); - } - } ); - return selected; - } - - return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes #9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } - - return target; -} - -/* Handles responses to an ajax request: - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes; - - // Remove auto dataType and get content-type in the process - while ( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -/* Chain conversions given the request and the original response - * Also sets the responseXXX fields on the jqXHR instance - */ -function ajaxConvert( s, response, jqXHR, isSuccess ) { - var conv2, current, conv, tmp, prev, - converters = {}, - - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(); - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - current = dataTypes.shift(); - - // Convert to each sequential dataType - while ( current ) { - - if ( s.responseFields[ current ] ) { - jqXHR[ s.responseFields[ current ] ] = response; - } - - // Apply the dataFilter if provided - if ( !prev && isSuccess && s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - prev = current; - current = dataTypes.shift(); - - if ( current ) { - - // There's only work to do if current dataType is non-auto - if ( current === "*" ) { - - current = prev; - - // Convert response if prev dataType is non-auto and differs from current - } else if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split( " " ); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.unshift( tmp[ 1 ] ); - } - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s.throws ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { - state: "parsererror", - error: conv ? e : "No conversion from " + prev + " to " + current - }; - } - } - } - } - } - } - - return { state: "success", data: response }; -} - -jQuery.extend( { - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {}, - - ajaxSettings: { - url: location.href, - type: "GET", - isLocal: rlocalProtocol.test( location.protocol ), - global: true, - processData: true, - async: true, - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - "*": allTypes, - text: "text/plain", - html: "text/html", - xml: "application/xml, text/xml", - json: "application/json, text/javascript" - }, - - contents: { - xml: /\bxml\b/, - html: /\bhtml/, - json: /\bjson\b/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText", - json: "responseJSON" - }, - - // Data converters - // Keys separate source (or catchall "*") and destination types with a single space - converters: { - - // Convert anything to text - "* text": String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": JSON.parse, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - url: true, - context: true - } - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - return settings ? - - // Building a settings object - ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : - - // Extending ajaxSettings - ajaxExtend( jQuery.ajaxSettings, target ); - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var transport, - - // URL without anti-cache param - cacheURL, - - // Response headers - responseHeadersString, - responseHeaders, - - // timeout handle - timeoutTimer, - - // Url cleanup var - urlAnchor, - - // Request state (becomes false upon send and true upon completion) - completed, - - // To know if global events are to be dispatched - fireGlobals, - - // Loop variable - i, - - // uncached part of the url - uncached, - - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - - // Callbacks context - callbackContext = s.context || s, - - // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && - ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, - - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - - // Status-dependent callbacks - statusCode = s.statusCode || {}, - - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - - // Default abort message - strAbort = "canceled", - - // Fake xhr - jqXHR = { - readyState: 0, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( completed ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; - } - } - match = responseHeaders[ key.toLowerCase() ]; - } - return match == null ? null : match; - }, - - // Raw string - getAllResponseHeaders: function() { - return completed ? responseHeadersString : null; - }, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( completed == null ) { - name = requestHeadersNames[ name.toLowerCase() ] = - requestHeadersNames[ name.toLowerCase() ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( completed == null ) { - s.mimeType = type; - } - return this; - }, - - // Status-dependent callbacks - statusCode: function( map ) { - var code; - if ( map ) { - if ( completed ) { - - // Execute the appropriate callbacks - jqXHR.always( map[ jqXHR.status ] ); - } else { - - // Lazy-add the new callbacks in a way that preserves old ones - for ( code in map ) { - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - var finalText = statusText || strAbort; - if ( transport ) { - transport.abort( finalText ); - } - done( 0, finalText ); - return this; - } - }; - - // Attach deferreds - deferred.promise( jqXHR ); - - // Add protocol if not provided (prefilters might expect it) - // Handle falsy url in the settings object (#10093: consistency with old signature) - // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ) - .replace( rprotocol, location.protocol + "//" ); - - // Alias method option to type as per ticket #12004 - s.type = options.method || options.type || s.method || s.type; - - // Extract dataTypes list - s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; - - // A cross-domain request is in order when the origin doesn't match the current origin. - if ( s.crossDomain == null ) { - urlAnchor = document.createElement( "a" ); - - // Support: IE <=8 - 11, Edge 12 - 13 - // IE throws exception on accessing the href property if url is malformed, - // e.g. http://example.com:80x/ - try { - urlAnchor.href = s.url; - - // Support: IE <=8 - 11 only - // Anchor's host property isn't correctly set when s.url is relative - urlAnchor.href = urlAnchor.href; - s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== - urlAnchor.protocol + "//" + urlAnchor.host; - } catch ( e ) { - - // If there is an error parsing the URL, assume it is crossDomain, - // it can be rejected by the transport if it is invalid - s.crossDomain = true; - } - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( completed ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) - fireGlobals = jQuery.event && s.global; - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Save the URL in case we're toying with the If-Modified-Since - // and/or If-None-Match header later on - // Remove hash to simplify url manipulation - cacheURL = s.url.replace( rhash, "" ); - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // Remember the hash so we can put it back - uncached = s.url.slice( cacheURL.length ); - - // If data is available, append data to url - if ( s.data ) { - cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; - - // #9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Add or update anti-cache param if needed - if ( s.cache === false ) { - cacheURL = cacheURL.replace( rantiCache, "$1" ); - uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; - } - - // Put hash and anti-cache on the URL that will be requested (gh-1732) - s.url = cacheURL + uncached; - - // Change '%20' to '+' if this is encoded form body content (gh-2658) - } else if ( s.data && s.processData && - ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { - s.data = s.data.replace( r20, "+" ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery.lastModified[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); - } - if ( jQuery.etag[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? - s.accepts[ s.dataTypes[ 0 ] ] + - ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { - - // Abort if not done already and return - return jqXHR.abort(); - } - - // Aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - completeDeferred.add( s.complete ); - jqXHR.done( s.success ); - jqXHR.fail( s.error ); - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - - // If request was aborted inside ajaxSend, stop there - if ( completed ) { - return jqXHR; - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = window.setTimeout( function() { - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - completed = false; - transport.send( requestHeaders, done ); - } catch ( e ) { - - // Rethrow post-completion exceptions - if ( completed ) { - throw e; - } - - // Propagate others as results - done( -1, e ); - } - } - - // Callback for when everything is done - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Ignore repeat invocations - if ( completed ) { - return; - } - - completed = true; - - // Clear timeout if it exists - if ( timeoutTimer ) { - window.clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Determine if successful - isSuccess = status >= 200 && status < 300 || status === 304; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // Convert no matter what (that way responseXXX fields are always set) - response = ajaxConvert( s, response, jqXHR, isSuccess ); - - // If successful, handle type chaining - if ( isSuccess ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - modified = jqXHR.getResponseHeader( "Last-Modified" ); - if ( modified ) { - jQuery.lastModified[ cacheURL ] = modified; - } - modified = jqXHR.getResponseHeader( "etag" ); - if ( modified ) { - jQuery.etag[ cacheURL ] = modified; - } - } - - // if no content - if ( status === 204 || s.type === "HEAD" ) { - statusText = "nocontent"; - - // if not modified - } else if ( status === 304 ) { - statusText = "notmodified"; - - // If we have data, let's convert it - } else { - statusText = response.state; - success = response.data; - error = response.error; - isSuccess = !error; - } - } else { - - // Extract error from statusText and normalize for non-aborts - error = statusText; - if ( status || !statusText ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = ( nativeStatusText || statusText ) + ""; - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - return jqXHR; - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - } -} ); - -jQuery.each( [ "get", "post" ], function( i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - - // Shift arguments if data argument was omitted - if ( jQuery.isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - // The url can be an options object (which then must have .url) - return jQuery.ajax( jQuery.extend( { - url: url, - type: method, - dataType: type, - data: data, - success: callback - }, jQuery.isPlainObject( url ) && url ) ); - }; -} ); - - -jQuery._evalUrl = function( url ) { - return jQuery.ajax( { - url: url, - - // Make this explicit, since user can override this through ajaxSetup (#11264) - type: "GET", - dataType: "script", - cache: true, - async: false, - global: false, - "throws": true - } ); -}; - - -jQuery.fn.extend( { - wrapAll: function( html ) { - var wrap; - - if ( this[ 0 ] ) { - if ( jQuery.isFunction( html ) ) { - html = html.call( this[ 0 ] ); - } - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map( function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - } ).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each( function( i ) { - jQuery( this ).wrapInner( html.call( this, i ) ); - } ); - } - - return this.each( function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - } ); - }, - - wrap: function( html ) { - var isFunction = jQuery.isFunction( html ); - - return this.each( function( i ) { - jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html ); - } ); - }, - - unwrap: function( selector ) { - this.parent( selector ).not( "body" ).each( function() { - jQuery( this ).replaceWith( this.childNodes ); - } ); - return this; - } -} ); - - -jQuery.expr.pseudos.hidden = function( elem ) { - return !jQuery.expr.pseudos.visible( elem ); -}; -jQuery.expr.pseudos.visible = function( elem ) { - return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); -}; - - - - -jQuery.ajaxSettings.xhr = function() { - try { - return new window.XMLHttpRequest(); - } catch ( e ) {} -}; - -var xhrSuccessStatus = { - - // File protocol always yields status code 0, assume 200 - 0: 200, - - // Support: IE <=9 only - // #1450: sometimes IE returns 1223 when it should be 204 - 1223: 204 - }, - xhrSupported = jQuery.ajaxSettings.xhr(); - -support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); -support.ajax = xhrSupported = !!xhrSupported; - -jQuery.ajaxTransport( function( options ) { - var callback, errorCallback; - - // Cross domain only allowed if supported through XMLHttpRequest - if ( support.cors || xhrSupported && !options.crossDomain ) { - return { - send: function( headers, complete ) { - var i, - xhr = options.xhr(); - - xhr.open( - options.type, - options.url, - options.async, - options.username, - options.password - ); - - // Apply custom fields if provided - if ( options.xhrFields ) { - for ( i in options.xhrFields ) { - xhr[ i ] = options.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( options.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( options.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Set headers - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - - // Callback - callback = function( type ) { - return function() { - if ( callback ) { - callback = errorCallback = xhr.onload = - xhr.onerror = xhr.onabort = xhr.onreadystatechange = null; - - if ( type === "abort" ) { - xhr.abort(); - } else if ( type === "error" ) { - - // Support: IE <=9 only - // On a manual native abort, IE9 throws - // errors on any property access that is not readyState - if ( typeof xhr.status !== "number" ) { - complete( 0, "error" ); - } else { - complete( - - // File: protocol always yields status 0; see #8605, #14207 - xhr.status, - xhr.statusText - ); - } - } else { - complete( - xhrSuccessStatus[ xhr.status ] || xhr.status, - xhr.statusText, - - // Support: IE <=9 only - // IE9 has no XHR2 but throws on binary (trac-11426) - // For XHR2 non-text, let the caller handle it (gh-2498) - ( xhr.responseType || "text" ) !== "text" || - typeof xhr.responseText !== "string" ? - { binary: xhr.response } : - { text: xhr.responseText }, - xhr.getAllResponseHeaders() - ); - } - } - }; - }; - - // Listen to events - xhr.onload = callback(); - errorCallback = xhr.onerror = callback( "error" ); - - // Support: IE 9 only - // Use onreadystatechange to replace onabort - // to handle uncaught aborts - if ( xhr.onabort !== undefined ) { - xhr.onabort = errorCallback; - } else { - xhr.onreadystatechange = function() { - - // Check readyState before timeout as it changes - if ( xhr.readyState === 4 ) { - - // Allow onerror to be called first, - // but that will not handle a native abort - // Also, save errorCallback to a variable - // as xhr.onerror cannot be accessed - window.setTimeout( function() { - if ( callback ) { - errorCallback(); - } - } ); - } - }; - } - - // Create the abort callback - callback = callback( "abort" ); - - try { - - // Do send the request (this may raise an exception) - xhr.send( options.hasContent && options.data || null ); - } catch ( e ) { - - // #14683: Only rethrow if this hasn't been notified as an error yet - if ( callback ) { - throw e; - } - } - }, - - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } -} ); - - - - -// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) -jQuery.ajaxPrefilter( function( s ) { - if ( s.crossDomain ) { - s.contents.script = false; - } -} ); - -// Install script dataType -jQuery.ajaxSetup( { - accepts: { - script: "text/javascript, application/javascript, " + - "application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /\b(?:java|ecma)script\b/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -} ); - -// Handle cache's special case and crossDomain -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - } -} ); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function( s ) { - - // This transport only deals with cross domain requests - if ( s.crossDomain ) { - var script, callback; - return { - send: function( _, complete ) { - script = jQuery( " - - - - - - - - - - -
-
-
-
- - -

Index

- -
- -
- - -
-
-
-
- -
-
- - - - \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/index.html b/ResourceFiles/html_page/_build/html/index.html deleted file mode 100644 index fdee20635..000000000 --- a/ResourceFiles/html_page/_build/html/index.html +++ /dev/null @@ -1,2230 +0,0 @@ - - - - - - - - Welcome to Osdag help! — Main Home | Help_Design_Examples 2021.01 documentation - - - - - - - - - - - - - - -
-
-
-
- - _images/website_header.png -
-

Welcome to Osdag help!

-
-
Osdag is a cross-platform free and open-source software for the design and detailing of steel structures, following relevant Indian Standards. Osdag is primarily built upon Python and other Python-based FOSS tools, such as, PyQt, OpenCascade, PythonOCC, SQLite, etc. It is developed by the Osdag team at IIT Bombay.
-

-
-
-
-

The OSI file of a sample design can be loaded through the File -> Load input option of the appropriate module.

-
-

Note

-

The examples highlighted inside a box are from the Release 2018-06-21 and older versions of Osdag. Please use the examples with the appropriate version of Osdag.

-
-
-

-
-
-

Osdag Homepage.

-
-
-

1. Connection

-
-

1.1. Shear Connection

-
-

1.1.1. Fin Plate

-

1.1.1.1 Column Flange-Beam Web (CFBW) connectivity

-
-

Sample Problem 1

-
-

Design a Fin Plate connection for a beam MB 350 connected to a column HB 450 to transfer a factored shear force of 180 kN and an axial force of 50 kN. -The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Diameter: 12, 16, 20, 24, 30; Grade: 4.6, 4.8, 5.6, 6.8, 8.8

  • -
  • Plate: Material E 350 (Fe 490); Thickness (mm): 10, 12, 16, 18, 20

  • -
  • Bolt hole type is standard and slip factor is 0.3

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 15 mm

  • -
-
-

Download:

-

DesignReport_1.1.1.1.1.pdf.

-

Example_1.1.1.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Fin Plate connection for a beam WB 300 connected to a column PBP 300 X 88.48 to transfer a factored shear force of 175 kN and an axial force of 35 kN. -The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20; Grade: 6.8

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are hand flame cut

  • -
  • Gap between the members is 10 mm

  • -
-
-

Download:

-

DesignReport_1.1.1.1.2.pdf.

-

Example_1.1.1.1.2.osi.

-
-
-

1.1.1.2. Column Web-Beam Web (CWBW) connectivity

-
-

Sample Problem 1

-
-

Design a Fin Plate connection for a beam NPB 350 X 250 X 79.18 connected to a column PBP 400 X 230.9 to transfer a factored shear force of 225 kN and an axial force of 100 kN. -The grade of the beam and the column material is E 350 (Fe 410 W)A respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 12, 16, 20, 24 ; Grade: 4.6, 8.8, 10.9

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 12, 14, 16, 18, 20, 22, 25

  • -
  • Bolt hole type is over-sized

  • -
  • The ultimate strength of the weld material is, fu = 510 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 12 mm

  • -
-
-

Download:

-

DesignReport_1.1.1.2.1.pdf.

-

Example_1.1.1.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Fin Plate connection for a beam WPB 300 X 300 X 100.85 connected to a column UC 356 X 368 X 129 to transfer a factored shear force of 140 kN. -The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16; Grade: 4.6

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 10 mm

  • -
-
-

Download:

-

DesignReport_1.1.1.2.2.pdf.

-

Example_1.1.1.2.2.osi.

-
-
-

1.1.1.3. Beam-Beam (BB) connectivity

-
-

Sample Problem 1

-
-

Design a Fin Plate connection for a primary beam WB 400 connected to a secondary beam MB 300 to transfer a factored shear force of 160 kN and an axial force of 20 kN. -The grade of the primary and secondary beam material is E 300 (Fe 440) respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Diameter: 16, 20, 24 ; Grade: 8.8, 10.9

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20, 22

  • -
  • Bolt hole type is standard

  • -
  • Surface treatment: Sand blast (slip factor = 0.48)

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 5 mm

  • -
-
-

Download:

-

DesignReport_1.1.1.3.1.pdf.

-

Example_1.1.1.3.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Fin Plate connection for a primary beam UB 305 x 102 x 33 connected to a secondary beam MB 300 to transfer a factored shear force of 100 kN. -The grade of the primary and secondary beam material is E 250 (Fe 410 W)A respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16; Grade: 5.8

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14

  • -
  • Bolt hole type is over-sized

  • -
  • The ultimate strength of the weld material is, fu = 450 MPa

  • -
  • The plate edges are hand flame cut

  • -
  • Gap between the members is 10 mm

  • -
-
-

Download:

-

DesignReport_1.1.1.3.2.pdf.

-

Example_1.1.1.3.2.osi.

-
-
-

Note

-

Example for Release 2018-06-21 and lower versions - Fin Plate

-

1.1.1.1 Column Flange-Beam Web (CFBW) connectivity

-
-

Sample Problem 1

-
-

Design a fin plate connection between a beam MB 500 and a column UC 305 x 305 x 97 for transferring a vertical (factored) shear force of 140 kN. Use M24 Friction grip bolts of grade 8.8. Try 12mm thick fin plate with weld thickness of 12mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, weld type as shop weld and type of edge as hand flame cut.

-
-

Download:

-

Design_Report_1.1.1.1.1.pdf.

-

Design_Example_1.1.1.1.1.osi.

-

Sample Problem 2

-
-

Design a fin plate connection between a beam MB 350 and a column SC 200 for transferring a vertical (factored) shear force of 150 kN. Use M20 bearing bolts of grade 4.6. Try 12mm thick fin plate with weld thickness of 8mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume weld type as shop weld and type of edge as machine flame cut.

-
-

Download:

-

Design_Report_1.1.1.1.2.pdf.

-

Design_Example_1.1.1.1.2.osi.

-
-
-
-
-

Note

-

Example for Release 2018-06-21 and lower versions - Fin Plate

-

1.1.1.2. Column Web-Beam Web (CWBW) connectivity

-
-

Sample Problem 1

-
-

Design a fin plate connection between a beam UB 356 x 171 x 45 and a column PBP 300 x 180 for transferring a vertical (factored) shear force of 120 kN. Use M16 Friction grip bolts of grade 8.8. Try 8mm thick fin plate with weld thickness of 8mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, slip factor of 0.25, type of weld as field weld and edge as hand flame cut.

-
-

Download:

-

Design_Report_1.1.1.2.1.pdf.

-

Design_Example_1.1.1.2.1.osi.

-

Sample Problem 2

-
-

Design a fin plate connection between a beam LB 300 and a column SC 250 for transferring a vertical (factored) shear force of 135 kN. Use M24 bearing bolts of grade 4.8. Try 10mm thick fin plate with weld thickness of 8mm. Take Fe410 grade steel fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, type of weld as shop weld and edge as hand flame cut and environment as corrosive.

-
-

Download:

-

Design_Report_1.1.1.2.2.pdf.

-

Design_Example_1.1.1.2.2.osi.

-
-
-
-
-

Note

-

Example for Release 2018-06-21 and lower versions - Fin Plate

-

1.1.1.3. Beam-Beam (BB) connectivity

-
-

Sample Problem 1

-
-

Design a fin plate connection between a primary beam MB 350 and a secondary beam NPB 270 x 135 x 36.1 for transferring a vertical (factored) shear force of 110 kN. Use M20 Friction grip bolts of grade 10.9. Try 10mm thick fin plate with weld thickness of 8mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.52, bolt hole type oversized and type of edge as machine flame cut.

-
-

Download:

-

Design_Report_1.1.1.3.1.pdf.

-

Design_Example_1.1.1.3.1.osi.

-

Sample Problem 2

-
-

Design a fin plate connection between a primary beam WPB 450 x 300 x 99.7 and a secondary beam UB 356 x 171 x 67 for transferring a vertical (factored) shear force of 220 kN. Use M24 Friction grip bolts of grade 10.9. Try 14mm thick fin plate with weld thickness of 12mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.48, bolt hole type standard, weld type as shop weld and type of edge as machine flame cut.

-
-

Download:

-

Design_Report_1.1.1.3.2.pdf.

-

Design_Example_1.1.1.3.2.osi.

-
-
-
-
-

1.1.2. End Plate

-

1.1.2.1. Column Flange-Beam Web (CFBW) connectivity

-
-

Sample Problem 1

-
-

Design an End Plate connection for a beam LB 400 connected to a column PBP 300 X 109.54 to transfer a factored shear force of 240 kN and an axial force of 125 kN. -The grade of the beam and the column material are E 250 (Fe 410 W)A and E 350 (Fe 490) respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16, 20, 24, 30; Grade: 4.8, 5.6, 6.8, 9.8

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14, 16, 18, 20, 22, 25, 28

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_1.1.2.1.1.pdf.

-

Example_1.1.2.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design an End Plate connection for a beam UB 356 x 127 x 39 connected to a column UB 254 x 254 x 89 to transfer a factored shear force of 250 kN and an axial force of 80 kN. -The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Diameter: 24; Grade: 8.8

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 18

  • -
  • Bolt hole type is standard and slip factor is 0.25

  • -
  • The plate edges are hand flame cut

  • -
-
-

Download:

-

DesignReport_1.1.2.1.2.pdf.

-

Example_1.1.2.1.2.osi.

-
-
-

1.1.2.2. Column Web-Beam Web (CWBW) connectivity

-
-

Sample Problem 1

-
-

Design an End Plate connection for a beam WB 300 connected to a column HB 350 to transfer a factored shear force of 220 kN and an axial force of 30 kN. -The grade of the beam and the column material is E 250 (Fe 410 W)A.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16, 20, 24; Grade: 4.6, 4.8, 5.6, 5.8

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14, 16, 20

  • -
  • Bolt hole type is over-sized

  • -
  • The ultimate strength of the weld material is, fu = 500 MPa

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_1.1.2.2.1.pdf.

-

Example_1.1.2.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design an End Plate connection for a beam MB 400 connected to a column HB 350 to transfer a factored shear force of 275 kN. -The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20; Grade: 9.8

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness(mm): 20

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are are machine-flame cut

  • -
-
-

Download:

-

DesignReport_1.1.2.2.2.pdf.

-

Example_1.1.2.2.2.osi.

-
-
-

1.1.2.3. Beam-Beam (BB) connectivity

-
-

Sample Problem 1

-
-

Design an End Plate connection for a primary beam WB 400 connected to a secondary beam MB 300 to transfer a factored shear force of 160 kN and an axial force of 20 kN. -The grade of the primary and secondary beam material is E 300 (Fe 440) respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Diameter: 16, 20, 24 ; Grade: 8.8, 10.9

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20, 22

  • -
  • Bolt hole type is standard

  • -
  • Surface treatment: Sand blast (slip factor = 0.48)

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_1.1.2.3.1.pdf.

-

Example_1.1.2.3.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design an End Plate connection for a primary beam UB 305 x 102 x 33 connected to a secondary beam MB 300 to transfer a factored shear force of 100 kN. -The grade of the primary and secondary beam material is E 250 (Fe 410 W)A respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16; Grade: 5.8

  • -
  • Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14

  • -
  • Bolt hole type is over-sized

  • -
  • The ultimate strength of the weld material is, fu = 450 MPa

  • -
  • The plate edges are hand flame cut

  • -
-
-

Download:

-

DesignReport_1.1.2.3.2.pdf.

-

Example_1.1.2.3.2.osi.

-
-
-

Note

-

Example for Release 2018-06-21 and lower versions - End Plate

-

1.1.2.1. Column Flange-Beam Web (CFBW) connectivity

-
-

Sample Problem 1

-
-

Design an end plate connection between a beam MB 350 and a column SC 250 for transferring a vertical (factored) shear force of 140 kN. Use M20 bearing bolts of grade 4.6. Try 10mm thick end plate with weld thickness of 6mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, weld type shop weld and type of edge as hand flame cut.

-
-

Download:

-

Design_Report_1.1.2.1.1.pdf.

-

Design_Example_1.1.2.1.1.osi.

-

Sample Problem 2

-
-

Design an end plate connection between a beam MB 300 and a column UC 254 x254 x 107 for transferring a vertical (factored) shear force of 195 kN. Use M16 Friction grip bolts of grade 8.8. Try 12mm thick end plate with weld thickness of 10mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, weld type shop weld and type of edge as machine flame cut.

-
-

Download:

-

Design_Report_1.1.2.1.2.pdf.

-

Design_Example_1.1.2.1.2.osi.

-
-
-
-
-

Note

-

Example for Release 2018-06-21 and lower versions - End Plate

-

1.1.2.2. Column Web-Beam Web (CWBW) connectivity

-
-

Sample Problem 1

-
-

Design an end plate connection between a beam UB 356 x 171 x 45 and a column PBP 300 x 180 for transferring a vertical (factored) shear force of 120 kN. Use M16 Friction grip bolts of grade 10.9. Try 12mm thick end plate with weld thickness of 6mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, slip factor of 0.25, type of weld as field weld and edge type as hand flame cut.

-
-

Download:

-

Design_Report_1.1.2.2.1.pdf.

-

Design_Example_1.1.2.2.1.osi.

-

Sample Problem 2

-
-

Design an end plate connection between a beam LB 300 and a column SC 250 for transferring a vertical (factored) shear force of 135 kN. Use M12 bearing bolts of grade 4.8. Try 10mm thick end plate with weld thickness of 10mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, type of weld as shop weld and edge type as hand flame cut.

-
-

Download:

-

Design_Report_1.1.2.2.2.pdf.

-

Design_Example_1.1.2.2.2.osi.

-
-
-
-
-

Note

-

Example for Release 2018-06-21 and lower versions - End Plate

-

1.1.2.3. Beam-Beam (BB) connectivity

-
-

Sample Problem 1

-
-

Design an end plate connection between a primary beam MB 500 and a secondary beam MB 400 for transferring a vertical (factored) shear force of 160 kN. Use M20 Friction grip bolts of grade 8.8. Try 16mm thick end plate with weld thickness of 8mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.2, bolt hole type as oversized, weld type shop weld and type of edge as machine flame cut.

-
-

Download:

-

Design_Report_1.1.2.3.1.pdf.

-

Design_Example_1.1.2.3.1.osi.

-

Sample Problem 2

-
-

Design an end plate connection between a primary beam WPB 450 x 300 x 99.7 and a secondary beam UB 356 x 171 x 67 for transferring a vertical (factored) shear force of 220 kN. Use M24 Friction grip bolts of grade 10.9. Try 14mm thick end plate with weld thickness of 12mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.48, bolt hole type as standard, weld type shop weld and type of edge as machine flame cut.

-
-

Download:

-

Design_Report_1.1.2.3.2.pdf.

-

Design_Example_1.1.2.3.2.osi.

-
-
-
-
-

1.1.3. Cleat Angle

-

1.1.3.1. Column Flange-Beam Web (CFBW) connectivity

-
-

Sample Problem 1

-
-

Design a Cleat Angle connection for a beam MB 300 connected to a column HB 250 to transfer a factored shear force of 130 kN. -The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16, 20; Grade: 4.6, 4.8

  • -
  • Angle: Material E 250 (Fe 410 W)A; Designation: 90 x 90 x 10, 90 x 90 x 12, 100 x 100 x 6, 100 x 100 x 8

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_1.1.3.1.1.pdf.

-

Example_1.1.3.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Cleat Angle connection for a beam MB 450 connected to a column UC 305 x 305 x 118 to transfer a factored shear force of 240 kN. -The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Diameter: 20; Grade: 12.9

  • -
  • Cleat Angle: Material E 250 (Fe 410 W)A; Designation: 120 x 120 x 8

  • -
  • Bolt hole type is standard

  • -
  • Surface treatment: Clean mill scale (slip factor = 0.33)

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_1.1.3.1.2.pdf.

-

Example_1.1.3.1.2.osi.

-
-
-

1.1.3.2. Column Web-Beam Web (CWBW) connectivity

-
-

Sample Problem 1

-
-

Design a Cleat Angle connection for a beam WPB 250 X 250 X 85.4 connected to a column PBP 300 X 95 to transfer a factored shear force of 170 kN. -The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16, 20, 24; Grade: 6.8, 8.8, 98

  • -
  • Angle: Material E 250 (Fe 410 W)A; Designation: All the sections available with the Osdag database

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_1.1.3.2.1.pdf.

-

Example_1.1.3.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Cleat Angle connection for a beam MB 450 connected to a column UC 305 x 305 x 118 to transfer a factored shear force of 240 kN. -The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Diameter: 20; Grade: 12.9

  • -
  • Cleat Angle: Material E 250 (Fe 410 W)A; Designation: 80 x 80 x 8

  • -
  • Bolt hole type is standard

  • -
  • Surface treatment: Clean mill scale (slip factor = 0.33)

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_1.1.3.2.2.pdf.

-

Example_1.1.3.2.2.osi.

-
-
-

1.1.3.3. Beam-Beam (BB) connectivity

-
-

Sample Problem 1

-
-

Design a Cleat Angle connection for a primary beam WB 400 connected to a secondary beam MB 300 to transfer a factored shear force of 160 kN. -The grade of the primary and secondary beam material is E 300 (Fe 440) respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Diameter: 16, 20, 24 ; Grade: 8.8, 12.9

  • -
  • Cleat Angle: Material E 165 (Fe 290); Designation: 80 x 80 x 8, 90 x 90 x 6, 90 x 90 x 8, 90 x 90 x 10, 90 x 90 x 12, 100 x 100 x 8, 100 x 100 x 10, 110 x 110 x 10

  • -
  • Bolt hole type is standard

  • -
  • Surface treatment: Sand blast (slip factor = 0.48)

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_1.1.3.3.1.pdf.

-

Example_1.1.3.3.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Cleat Angle connection for a primary beam UB 305 x 102 x 33 connected to a secondary beam MB 300 to transfer a factored shear force of 100 kN. -The grade of the primary and secondary beam material is E 250 (Fe 410 W)A respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16; Grade: 5.8

  • -
  • Cleat Angle: Material E 250 (Fe 410 W)A; Designation: 130 x 130 x 10

  • -
  • Bolt hole type is over-sized

  • -
  • The plate edges are hand flame cut

  • -
-
-

Download:

-

DesignReport_1.1.3.3.2.pdf.

-

Example_1.1.3.3.2.osi.

-
-
-

Note

-

Examples for Release 2018-06-21 and lower versions - Cleat Angle

-

1.1.3.1. Column Flange-Beam Web (CFBW) connectivity

-
-

Sample Problem 1

-
-

Design a cleat angle connection between a beam MB 400 and a column SC 250 for transferring a vertical (factored) shear force of 140 kN. Use M20 Friction grip bolts of grade 8.8. Try cleat angle of size 90 x 90 x 12. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.48, bolt hole type as standard and type of edge as hand flame cut.

-
-

Download:

-

Design_Report_1.1.3.1.1.pdf.

-

Design_Example_1.1.3.1.1.osi.

-

Sample Problem 2

-
-

Design a cleat angle connection between a beam MB 350 and a column HB 300 for transferring a vertical (factored) shear force of 170 kN. Use M16 bearing bolts of grade 4.6. Try cleat angle of size 100 x 100 x 12. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, type of edge as machine flame cut and environment as corrosive.

-
-

Download:

-

Design_Report_1.1.3.1.2.pdf.

-

Design_Example_1.1.3.1.2.osi.

-
-
-
-
-

Note

-

Examples for Release 2018-06-21 and lower versions - Cleat Angle

-

1.1.3.2. Column Web-Beam Web (CWBW) connectivity

-
-

Sample Problem 1

-
-

Design a cleat angle connection between a beam MB 350 and a column UC 305 x 305 x 97 for transferring a vertical (factored) shear force of 120.5 kN. Use M20 Friction grip bolts of grade 8.8. Try cleat angle of size 110 x 110 x 16. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, slip factor of 0.2, type of edge as hand flame cut and gap between column and beam as 5mm.

-
-

Download:

-

Design_Report_1.1.3.2.1.pdf.

-

Design_Example_1.1.3.2.1.osi.

-

Sample Problem 2

-
-

Design a cleat angle connection between a beam MB 200 and a column UC 305 x 305 x 118 for transferring a vertical (factored) shear force of 80 kN. Use M12 bearing bolts of grade 6.8. Try cleat angle of size 110 x 110 x 16. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, type of edge as hand flame cut and environment as corrosive.

-
-

Download:

-

Design_Report_1.1.3.2.2.pdf.

-

Design_Example_1.1.3.2.2.osi.

-
-
-
-
-

Note

-

Examples for Release 2018-06-21 and lower versions - Cleat Angle

-

1.1.3.3. Beam-Beam (BB) connectivity

-
-

Sample Problem 1

-
-

Design a cleat angle connection between a primary beam WB 450 and a secondary beam MB 400 for transferring a vertical (factored) shear force of 145 kN. Use M24 bearing bolts of grade 4.6. Try cleat angle of size 100 100 x 10. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard and type of edge as hand flame cut.

-
-

Download:

-

Design_Report_1.1.3.3.1.pdf.

-

Design_Example_1.1.3.3.1.osi.

-

Sample Problem 2

-
-

Design a cleat angle connection between a primary beam WPB 280x280x61.2 and a secondary beam NPB 220x110x29.4 for transferring a vertical (factored) shear force of 100 kN. Use M20 Friction grip bolts of grade 10.9. Try cleat angle of size 100 100 x 8. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as oversized, slip factor of 0.52, type of edge as machine flame cut and gap between column and beam as 15mm.

-
-

Download:

-

Design_Report_1.1.3.3.2.pdf.

-

Design_Example_1.1.3.3.2.osi.

-
-
-
-
-

1.1.4. Seated Angle

-

1.1.4.1. Column Flange-Beam Flange (CFBF) connectivity

-
-

Sample Problem 1

-
-

Design a Seated Angle connection for a beam WB 400 connected to a column PBP 360 X 152.2 to transfer a factored shear force of 210 kN. -The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24, 30; Grade: 4.6, 4.8, 5.6, 5.8, 6.8, 8.8, 9.8

  • -
  • Seated Angle: Material E 300 (Fe 440); Designation: All the sections available with the Osdag database

  • -
  • Top Angle: Material E 300 (Fe 440); Designation: All the sections available with the Osdag database

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 10 mm

  • -
-
-

Download:

-

DesignReport_1.1.4.1.1.pdf.

-

Example_1.1.4.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Seated Angle connection for a beam MB 500 connected to a column UC 305 x 305 x 118 to transfer a factored shear force of 185 kN. -The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Diameter: 20; Grade: 12.9

  • -
  • Seated Angle: Material E 250 (Fe 410 W)A; Designation: 150 x 150 x 10

  • -
  • Top Angle: Material E 250 (Fe 410 W)A; Designation: 150 x 150 x 10

  • -
  • Bolt hole type is standard

  • -
  • Surface treatment: Blasted with short or grit, loose rust removed (slip factor = 0.5)

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 10 mm

  • -
-
-

Download:

-

DesignReport_1.1.4.1.2.pdf.

-

Example_1.1.4.1.2.osi.

-
-
-

1.1.4.2. Column Web-Beam Flange (CWBF) connectivity

-
-

Sample Problem 1

-
-

Design a Seated Angle connection for a beam MB 400 connected to a column HB 450 to transfer a factored shear force of 230 kN. -The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24, 30; Grade: 4.6, 4.8, 5.6, 5.8, 6.8, 8.8, 9.8

  • -
  • Seated Angle: Material E 250 (Fe 410 W)A; Designation: All the sections available with the Osdag database

  • -
  • Top Angle: Material E 250 (Fe 410 W)A; Designation: All the sections available with the Osdag database

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 10 mm

  • -
-
-

Download:

-

DesignReport_1.1.4.2.1.pdf.

-

Example_1.1.4.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Seated Angle connection for a beam LB 325 connected to a column UC 203 x 203 x 52 to transfer a factored shear force of 90 kN. -The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16; Grade: 8.8

  • -
  • Seated Angle: Material E 250 (Fe 410 W)A; Designation: 90 x 90 x 10

  • -
  • Top Angle: Material E 250 (Fe 410 W)A; Designation: 80 x 80 x 10

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 10 mm

  • -
-
-

Download:

-

DesignReport_1.1.4.2.2.pdf.

-

Example_1.1.4.2.2.osi.

-
-
-

Note

-

Examples for Release 2018-06-21 and lower versions - Seated Angle

-

1.1.4.1. Column Flange-Beam Flange (CFBF) connectivity

-
-

Sample Problem 1

-
-

Design a seated angle connection between a beam MB 300 and a column UC 203 x 203 x 86 for transferring a vertical (factored) shear force of 100 kN. Use M20 Friction grip bolts of grade 10.9. Try a top angle of size 150 150 x 10 and seated angle of size 150 150 x 15. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.55, bolt fu = 940 MPa and type of edge as hand flame cut.

-
-

Download:

-

Design_Report_1.1.4.1.1.pdf.

-

Design_Example_1.1.4.1.1.osi.

-

Sample Problem 2

-
-

Design a seated angle connection between a beam MB 200 and a column SC 140 for transferring a vertical (factored) shear force of 140 kN. Use M12 bearing bolts of grade 6.8. Try a top angle of size 90 90 x 8 and seated angle of size 110 110 x 16. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume type of edge as hand flame cut and environment as corrosive.

-
-

Download:

-

Design_Report_1.1.4.1.2.pdf.

-

Design_Example_1.1.4.1.2.osi.

-
-
-
-
-

Note

-

Examples for Release 2018-06-21 and lower versions - Seated Angle

-

1.1.4.2. Column Web-Beam Flange (CWBF) connectivity

-
-

Sample Problem 1

-
-

Design a seated angle connection between a beam NPB 250x150x39.8 and a column PBP 300x180 for transferring a vertical (factored) shear force of 80 kN. Use M16 bearing bolts of grade 5.8. Try a top angle of size 90 90 x 10 and seated angle of size 150 150 x 15. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume type of edge as machine flame cut.

-
-

Download:

-

Design_Report_1.1.4.2.1.pdf.

-

Design_Example_1.1.4.2.1.osi.

-

Sample Problem 2

-
-

Design a seated angle connection between a beam WPB 140x140x24.7 and a column HB 200 for transferring a vertical (factored) shear force of 140 kN. Use M12 bearing bolts of grade 5.8. Try a top angle of size 90 90 x 10 and seated angle of size 150 150 x 15. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as Oversized, type of edge as machine flame cut and gap between column and beam as 5mm.

-
-

Download:

-

Design_Report_1.1.4.2.2.pdf.

-

Design_Example_1.1.4.2.2.osi.

-
-
-
-
-
-

1.2. Moment Connection

-
-

1.2.1. Beam-to-Beam Splice Connection

-
-
1.2.1.1. Cover Plate Bolted
-
-

Sample Problem 1

-
-

Design a Beam-Beam Cover Plate Splice connection for a beam UB 610 x 305 x 179. The grade of the beam material is E 300 (Fe 440).

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 300 kNm (reversible)

  • -
  • Shear Force: 160 kN

  • -
  • Axial Force: 40 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24, 30, 36; Grade: 6.8, 8.8, 9.8

  • -
  • Flange Splice Plate: Material E 250 (Fe 410 W)A; Preference: Outside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Web Splice Plate: Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.1.1.1.pdf.

-

Example_1.2.1.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Beam Cover Plate Splice connection for a beam WPB 900 x 300 x 198.01. The grade of the beam material is E 350 (Fe 490).

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 395 kNm (reversible)

  • -
  • Shear Force: 110 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 24; Grade: 8.8

  • -
  • Flange Splice Plate: Material E 300 (Fe 440); Preference: Outside + Inside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Web Splice Plate: Material E 300 (Fe 440); Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Bolt hole type is over-sized

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.1.1.2.pdf.

-

Example_1.2.1.1.2.osi.

-
-
-
-

Note

-

Example for Release 2018-06-21 and lower versions - Cover Plate Bolted Connection

-

Sample Problem 1

-
-

Design a bolted cover plate splice connection for beam MB 450 to transfer a factored external moment of 140 kNm, factored shear of 110 kN and factored axial force of 40 kN. Use M20 Friction grip bolts of grade 8.8. Try 20mm thick flange splice plate and 10mm thick web splice plate. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, edge as hand flame cut, gap between the beams as 5mm and surface of the metal to be treated with sand blast.

-
-

Download:

-

Design_Report_1.2.1.1.1.1.pdf.

-

Design_Example_1.2.1.1.1.1.osi.

-

Sample Problem 2

-
-

Design a bolted cover plate splice connection for beam NPB 350 x 250 x 79.2 to transfer a factored external moment and shear force of 225 kNm ad 55 kN. Use M24 Friction grip bolts of grade 10.9. Try 14mm thick flange splice plate and 6mm thick web splice plate. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as oversized, edge as machine-flame cut, slip factor as 0.33 and gap between the beams as 3mm.

-
-

Download:

-

Design_Report_1.2.1.1.1.2.pdf.

-

Design_Example_1.2.1.1.1.2.osi.

-
-
-
-
1.2.1.2. End Plate
-

1.2.1.2.1. Coplanar Tension-Compression Flange

-

1.2.1.2.1.1. Flushed - Reversible Moment

-
-

Sample Problem 1

-
-

Design a Beam-Beam End Plate Splice connection for a beam WB 500. The grade of the beam material is E 250 (Fe 410 W)A.

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 250 kNm (reversible)

  • -
  • Shear Force: 120 kN

  • -
  • Axial Force: 35 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24, 30; Grade: 6.8, 8.8

  • -
  • End Plate: Material E 300 (Fe 440); Thickness (mm): 20, 22, 25, 28, 32

  • -
  • Bolt hole type is standard

  • -
  • The ultimate strength of the weld material is, fu = 500 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.1.2.1.1.1.pdf.

-

Example_1.2.1.2.1.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Beam End Plate Splice connection for a beam MB 400. The grade of the beam material is E 250 (Fe 410 W)A.

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 100 kNm (reversible)

  • -
  • Shear Force: 40 kN

  • -
  • Axial Force: 20 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Pre-tensioned; Diameter: 16; Grade: 12.9

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 22

  • -
  • Bolt hole type is standard

  • -
  • Surface treatment: Sand blast (slip factor = 0.48)

  • -
  • The ultimate strength of the weld material is, fu = 450 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are exposed to marine environment

  • -
-
-

Download:

-

DesignReport_1.2.1.2.1.1.2.pdf.

-

Example_1.2.1.2.1.1.2.osi.

-
-
-

1.2.1.2.1.2. Extended One way - Irreversible Moment

-
-

Sample Problem 1

-
-

Design a Beam-Beam End Plate Splice connection for a beam UB 610 x 229 x 125. The grade of the beam material is E 300 (Fe 440).

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 300 kNm (irreversible)

  • -
  • Shear Force: 140 kN

  • -
  • Axial Force: 50 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24, 30; Grade: 8.8, 10.9

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 18, 20, 22

  • -
  • Bolt hole type is standard

  • -
  • The ultimate strength of the weld material is, fu = 410 MPa

  • -
  • The plate edges are hand flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.1.2.1.2.1.pdf.

-

Example_1.2.1.2.1.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Beam End Plate Splice connection for a beam LB 400. The grade of the beam material is E 250 (Fe 410 W)A.

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 110 kNm (irreversible)

  • -
  • Shear Force: 55 kN

  • -
  • Axial Force: 15 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20; Grade: 5.8

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 12

  • -
  • Bolt hole type is standard

  • -
  • The ultimate strength of the weld material is, fu = 410 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.1.2.1.2.2.pdf.

-

Example_1.2.1.2.1.2.2.osi.

-
-
-

1.2.1.2.1.3. Extended Both Ways - Reversible Moment

-
-

Sample Problem 1

-
-

Design a Beam-Beam End Plate Splice connection for a beam WPB 360 X 300 X 91.4. The grade of the beam material is E 250 (Fe 410 W)A.

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 120 kNm (reversible)

  • -
  • Shear Force: 75 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24; Grade: 4.6, 4.8, 5.6, 5.8

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16

  • -
  • Bolt hole type is standard

  • -
  • The ultimate strength of the weld material is, fu = 410 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.1.2.1.3.1.pdf.

-

Example_1.2.1.2.1.3.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Beam End Plate Splice connection for a beam UB 406 x 140 x 46. The grade of the beam material is E 300 (Fe 440).

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 85 kNm (reversible)

  • -
  • Shear Force: 40 kN

  • -
  • Axial Force: 12 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Pre-tensioned; Diameter: 16; Grade: 12.9

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14

  • -
  • Bolt hole type is standard and bot type is Pre-tensioned

  • -
  • Surface treatment: Sand blast (slip factor = 0.48)

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.1.2.1.3.2.pdf.

-

Example_1.2.1.2.1.3.2.osi.

-
-
-

Note

-

Example for Release 2018-06-21 and lower versions - End Plate Splice Connection (Extended both ways)

-

Sample Problem 1

-
-

Design a bolted extended (both way) end plate spliced connection for a beam NPB 350x170x50.2, for transferring a factored reversible moment and shear force of 100 kNm and 40 kN. Use M20 bearing bolts of grade 9.8. Try an end plate 20 mm thick and weld of sizes 8 mm and 6 mm at flange and web, respectively. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). The bearing bolt is non-pretensioned and the bolt hole type is oversized. The type of weld is shop weld and the edge type is sheared or hand flame cut.

-
-

Download:

-

Design_Report_1.2.1.2.1.1.pdf.

-

Design_Example_1.2.1.2.1.1.osi.

-

Sample Problem 2

-
-

Design a bolted extended (both way) end plate spliced connection for a beam MB 450, for transferring a factored reversible moment, shear force and axial force of 170 kNm, 100 kN and 40 kN, respectively. Use M20 friction grip bolts of grade 8.8. Try an end plate 12 mm thick. The size of weld at flange and web is 6 mm and 8 mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume the bolts to be pre-tensioned, bolt hole type is standard and the slip factor is 0.3. The type of weld is shop weld and the edge type is sheared or hand flame cut.

-
-

Download:

-

Design_Report_1.2.1.2.1.2.pdf.

-

Design_Example_1.2.1.2.1.2.osi.

-
-
-
-
1.2.1.3. Cover Plate Welded
-
-

Sample Problem 1

-
-

Design a Beam-Beam Cover Plate Splice connection for a beam UB 686 x 254 x 140. The grade of the beam material is E 300 (Fe 440).

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 235 kNm (reversible)

  • -
  • Shear Force: 150 kN

  • -
  • Axial Force: 20 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Flange Splice Plate: Material E 250 (Fe 410 W)A; Preference: Outside + Inside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Web Splice Plate: Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Type of weld fabrication: Field weld

  • -
  • The ultimate strength of the weld material is, fu = 550 MPa

  • -
  • Gap between the members is 5 mm

  • -
  • Members are exposed to (mild) marine environment

  • -
-
-

Download:

-

DesignReport_1.2.1.3.1.pdf.

-

Example_1.2.1.3.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Beam Cover Plate Splice connection for a beam WB 400. The grade of the beam material is E 250 (Fe 410 W)A.

-

The beam is subjected to the following loading condition;

-
    -
  • Bending Moment: 150 kNm (reversible)

  • -
  • Shear Force: 80 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Flange Splice Plate: Material E 300 (Fe 440); Preference: Outside + Inside; Thickness (mm): 18

  • -
  • Web Splice Plate: Material E 300 (Fe 440); Thickness (mm): 12

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 450 MPa

  • -
  • Gap between the members is 8 mm

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.2.1.3.2.pdf.

-

Example_1.2.1.3.2.osi.

-
-
-
-
-

1.2.2. Beam-to-Column Connection

-
-
1.2.2.1. End Plate
-

1.2.2.1.1. Column Flange-Beam Web (CFBW) connectivity

-

1.2.2.1.1.1. Flushed - Reversible Moment

-
-

Sample Problem 1

-
-

Design a Beam-Column End Plate connection for a beam WB 500 connected to a column PBP 400 X 140.2. The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 220 kNm (reversible)

  • -
  • Shear Force: 95 kN

  • -
  • Axial Force: 32 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 24, 30; Grade: 10.9, 12.9

  • -
  • End Plate: Material E 300 (Fe 440); Thickness (mm): 20, 22, 25, 28, 32

  • -
  • Bolt hole type is standard

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 510 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.2.2.1.1.1.1.pdf.

-

Example_1.2.2.1.1.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Column End Plate connection for a beam MB 500 connected to a column UC 305 x 305 x 118. The grade of the beam and the column material are E 250 (Fe 410 W)A and E 300 (Fe 440) respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 150 kNm (reversible)

  • -
  • Shear Force: 90 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Pre-tensioned; Diameter: 20; Grade: 12.9

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 28

  • -
  • Bolt hole type is standard

  • -
  • Surface treatment: Sand blast (slip factor = 0.48)

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 410 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are exposed to marine environment

  • -
-
-

Download:

-

DesignReport_1.2.2.1.1.1.2.pdf.

-

Example_1.2.2.1.1.1.2.osi.

-
-
-

1.2.2.1.1.2. Extended One way - Irreversible Moment

-
-

Sample Problem 1

-
-

Design a Beam-Column End Plate connection for a beam WB 450 connected to a column HB 450. The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 210 kNm (reversible)

  • -
  • Shear Force: 40 kN

  • -
  • Axial Force: 15 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24, 30; Grade: 6.8, 8.8, 9.8

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20, 22

  • -
  • Bolt hole type is standard

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 410 MPa

  • -
  • The plate edges are hand flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.2.2.1.1.2.1.pdf.

-

Example_1.2.2.1.1.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Column End Plate connection for a beam LB 450 connected to a column PBP 300 X 124.2. The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 100 kNm (reversible)

  • -
  • Shear Force: 55 kN

  • -
  • Axial Force: 12 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 24; Grade: 10.9

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16

  • -
  • Bolt hole type is standard

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 410 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.2.1.1.2.2.pdf.

-

Example_1.2.2.1.1.2.2.osi.

-
-
-

1.2.2.1.1.3. Extended Both Ways - Reversible Moment

-
-

Sample Problem 1

-
-

Design a Beam-Column End Plate connection for a beam MB 500 connected to a column PBP 400 X 158.1. The grade of the beam and the column material are E 300 (Fe 440) and E 350 (Fe 490) respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 180 kNm (reversible)

  • -
  • Shear Force: 100 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24, 30; Grade: 6.8, 8.8, 9.8

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16, 20, 22

  • -
  • Bolt hole type is standard

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 590 MPa

  • -
  • The plate edges are hand flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are exposed to marine environment

  • -
-
-

Download:

-

DesignReport_1.2.2.1.1.3.1.pdf.

-

Example_1.2.2.1.1.3.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Column End Plate connection for a beam LB(P) 300 connected to a column HB 300. The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 90 kNm (reversible)

  • -
  • Shear Force: 45 kN

  • -
  • Axial Force: 10 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Pre-tensioned; Diameter: 16; Grade: 8.8

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 18

  • -
  • Bolt hole type is standard and bot type is Pre-tensioned

  • -
  • Surface treatment: Clean mill scale (slip factor = 0.33)

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.2.1.1.3.2.pdf.

-

Example_1.2.2.1.1.3.2.osi.

-
-
-

1.2.2.1.2. Column Web-Beam Web (CWBW) connectivity

-

1.2.2.1.2.1. Flushed - Reversible Moment

-
-

Sample Problem 1

-
-

Design a Beam-Column End Plate connection for a beam WB 300 connected to a column HB 450. The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 75 kNm (reversible)

  • -
  • Shear Force: 50 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 16, 20, 24, 30; Grade: 8.8, 9.8, 10.9

  • -
  • End Plate: Material E 300 (Fe 440); Thickness (mm): 14, 16, 18, 20

  • -
  • Bolt hole type is standard

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 490 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.2.1.2.1.1.pdf.

-

Example_1.2.2.1.2.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Column End Plate connection for a beam MB 300 connected to a column UC 305 x 305 x 118. The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 45 kNm (reversible)

  • -
  • Shear Force: 70 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20; Grade: 9.8

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Bolt hole type is standard

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 435 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are exposed to marine environment

  • -
-
-

Download:

-

DesignReport_1.2.2.1.2.1.2.pdf.

-

Example_1.2.2.1.2.1.2.osi.

-
-
-

1.2.2.1.2.2. Extended One way - Irreversible Moment

-
-

Sample Problem 1

-
-

Design a Beam-Column End Plate connection for a beam NPB 300 x 200 x 59.57 connected to a column PBP 400 X 122.4. The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 73 kNm (irreversible)

  • -
  • Shear Force: 50 kN

  • -
  • Axial Force: 15 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24; Grade: 8.8, 9.8

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14, 16, 18

  • -
  • Bolt hole type is standard

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 440 MPa

  • -
  • The plate edges are hand flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.2.1.2.2.1.pdf.

-

Example_1.2.2.1.2.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Column End Plate connection for a beam LB 400 connected to a column PBP 400 X 122.4. The grade of the beam and the column material is E 300 (Fe 440) respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 110 kNm (irreversible)

  • -
  • Shear Force: 55 kN

  • -
  • Axial Force: 15 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20; Grade: 4.8

  • -
  • End Plate: E 300 (Fe 440); Thickness (mm): 16

  • -
  • Bolt hole type is standard

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 450 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.2.1.2.2.2.pdf.

-

Example_1.2.2.1.2.2.2.osi.

-
-
-

1.2.2.1.2.3. Extended Both Ways - Reversible Moment

-
-

Sample Problem 1

-
-

Design a Beam-Column End Plate connection for a beam UB 305 x 165 x 40 connected to a column UC 356 x 406 x 235. The grade of the beam and the column material are E 300 (Fe 440) and E 350 (Fe 490) respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 65 kNm (reversible)

  • -
  • Shear Force: 150 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Pre-tensioned; Diameter: 24; Grade: 8.8

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20

  • -
  • Bolt hole type is standard and slip factor is 0.3

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 450 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.2.1.2.3.1.pdf.

-

Example_1.2.2.1.2.3.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Beam-Column End Plate connection for a beam MB 500 connected to a column PBP 400 X 212.5. The grade of the beam and the column material is E 250 (Fe 410 W)A respectively.

-

The load transferred from the beam to the column is as follows;

-
    -
  • Bending Moment: 85 kNm (reversible)

  • -
  • Shear Force: 120 kN

  • -
  • Axial Force: 18 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Pre-tensioned; Diameter: 20; Grade: 8.8

  • -
  • End Plate: Material E 300 (Fe 440); Thickness (mm): 20

  • -
  • Bolt hole type is standard and bot type is Pre-tensioned

  • -
  • Surface treatment: Blasted with short or girt (slip factor = 0.5)

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 500 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
-
-

Download:

-

DesignReport_1.2.2.1.2.3.2.pdf.

-

Example_1.2.2.1.2.3.2.osi.

-
-
-
-
-

1.2.3. Column-to-Column Splice Connection

-
-
1.2.3.1. Cover Plate Bolted
-
-

Sample Problem 1

-
-

Design a Column-Column Cover Plate bolted connection for a column HB 300. The grade of the column material is E 300 (Fe 440).

-

The column is subjected to the following loading condition;

-
    -
  • Bending Moment: 127 kNm (reversible)

  • -
  • Axial Force: 320 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 20, 24, 30; Grade: 10.9, 12.9

  • -
  • Flange Splice Plate: Material E 300 (Fe 440); Preference: Outside + Inside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Web Splice Plate: Material E 300 (Fe 440); Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Bolt hole type is over-sized

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.2.3.1.1.pdf.

-

Example_1.2.3.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Column-Column Cover Plate bolted connection for a column PBP 400 X 176.1. The grade of the column material is E 300 (Fe 440).

-

The column is subjected to the following loading condition;

-
    -
  • Bending Moment: 60 kNm (reversible)

  • -
  • Shear Force: 40 kN

  • -
  • Axial Force: 520 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Pre-tensioned; Diameter: 24; Grade: 8.8

  • -
  • Flange Splice Plate: Material E 300 (Fe 440); Preference: Outside; Thickness (mm): 20

  • -
  • Web Splice Plate: Material E 300 (Fe 440); Thickness (mm): 16

  • -
  • Bolt hole type is standard

  • -
  • Surface treatment: Sand blasted (slip factor = 0.52)

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are exposed to marine environment

  • -
-
-

Download:

-

DesignReport_1.2.3.1.2.pdf.

-

Example_1.2.3.1.2.osi.

-
-
-
-
1.2.3.2. Cover Plate Welded
-
-

Sample Problem 1

-
-

Design a Column-Column Cover Plate welded connection for a column PBP 400 X 140.2. The grade of the column material is E 300 (Fe 440).

-

The column is subjected to the following loading condition;

-
    -
  • Bending Moment: 127 kNm (reversible)

  • -
  • Axial Force: 370 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Flange Splice Plate: Material E 300 (Fe 440); Preference: Outside + Inside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Web Splice Plate: Material E 300 (Fe 440); Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Type of weld fabrication: Field weld

  • -
  • The ultimate strength of the weld material is, fu = 540 MPa

  • -
  • Gap between the members is 3 mm

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.2.3.2.1.pdf.

-

Example_1.2.3.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Column-Column Cover Plate welded connection for a column HB 400. The grade of the column material is E 250 (Fe 410 W)A.

-

The column is subjected to the following loading condition;

-
    -
  • Bending Moment: 50 kNm (reversible)

  • -
  • Shear Force: 30 kN

  • -
  • Axial Force: 480 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Flange Splice Plate: Material E 250 (Fe 410 W)A; Preference: Outside; Thickness (mm): 18

  • -
  • Web Splice Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14

  • -
  • Type of weld fabrication: Field weld

  • -
  • The ultimate strength of the weld material is, fu = 510 MPa

  • -
  • Gap between the members is 5 mm

  • -
  • Members are exposed to (mild) marine environment

  • -
-
-

Download:

-

DesignReport_1.2.3.2.2.pdf.

-

Example_1.2.3.2.2.osi.

-
-
-
-
1.2.3.3. End Plate
-

1.2.3.3.1. Flush End Plate

-
-

Sample Problem 1

-
-

Design a Column-Column End Plate connection for a column HB 250. The grade of the column material is E 250 (Fe 410 W)A.

-

The column is subjected to the following loading condition;

-
    -
  • Axial Force: 350 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 24, 30; Grade: 8.8, 9.8, 10.9, 12.9

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.2.3.3.1.1.pdf.

-

Example_1.2.3.3.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Column-Column End Plate connection for a column PBP 300 X 95. The grade of the column material is E 300 (Fe 440).

-

The column is subjected to the following loading condition;

-
    -
  • Bending Moment: 50 kNm (reversible)

  • -
  • Shear Force: 20 kN

  • -
  • Axial Force: 250 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 30; Grade: 9.8

  • -
  • End Plate: Material E 300 (Fe 440); Thickness (mm): 32

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.2.3.3.1.2.pdf.

-

Example_1.2.3.3.1.2.osi.

-
-
-

1.2.3.3.2. Extended Both Ways

-
-

Sample Problem 1

-
-

Design a Column-Column End Plate connection for a column HB 250. The grade of the column material is E 250 (Fe 410 W)A.

-

The column is subjected to the following loading condition;

-
    -
  • Axial Force: 350 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Bolt type: Bearing bolt; Diameter: 24, 30; Grade: 8.8, 9.8, 10.9, 12.9

  • -
  • End Plate: Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.2.3.3.2.1.pdf.

-

Example_1.2.3.3.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Column-Column End Plate connection for a column PBP 300 X 95. The grade of the column material is E 300 (Fe 440).

-

The column is subjected to the following loading condition;

-
    -
  • Bending Moment: 50 kNm (reversible)

  • -
  • Shear Force: 20 kN

  • -
  • Axial Force: 250 kN

  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Bolt type: Friction grip (HSFG); Pre-tensioned; Diameter: 30; Grade: 9.8

  • -
  • End Plate: Material E 300 (Fe 440); Thickness (mm): 32

  • -
  • Bolt hole type is standard

  • -
  • The plate edges are machine-flame cut

  • -
  • Gap between the members is 0 mm

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.2.3.3.2.2.pdf.

-

Example_1.2.3.3.2.2.osi.

-
-
-
-
-
-

1.3. Base Plate Connection

-

1.3.1. Welded Column Base

-
-

Sample Problem 1

-
-

Design a Base Plate connection for a column HB 450 subjected to the following reactions at the base;

-
    -
  • Axial Compression: 1100 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Column: Grade of the column material is E 250 (Fe 410 W)A.

  • -
  • Anchor Bolt: Type: End Plate Type; Diameter: M20, M24, M30; Grade: 8.8, 9.8

  • -
  • Footing: Grade of concrete is M 25

  • -
  • Base Plate: Material E 250 (Fe 410 W)A

  • -
  • Bolt hole type is over-sized and the Anchor bolt is Galvanized

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 510 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Members are exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.3.1.1.pdf.

-

Example_1.3.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Base Plate connection for a column PBP 360 X 178.4 subjected to the following reactions at the base;

-
    -
  • Axial Compression: 880 kN

  • -
  • -
    Shear Force (at base):
      -
    • Along major (z-z) axis: 25 kN

    • -
    • Along minor (y-y) axis: 10 kN

    • -
    -
    -
    -
  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Column: Grade of the column material is E 300 (Fe 440).

  • -
  • Anchor Bolt: Type: End Plate Type; Diameter: M20; Grade: 10.9

  • -
  • Footing: Grade of concrete is M 30

  • -
  • Base Plate: Material E 250 (Fe 410 W)A

  • -
  • Stiffener/Shear Key: Material E 250 (Fe 410 W)A

  • -
  • Bolt hole type is over-sized and the Anchor bolt is Galvanized

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 440 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Members are exposed to corrosive environment

  • -
-
-

Download:

-

DesignReport_1.3.1.2.pdf.

-

Example_1.3.1.2.osi.

-
-
-

1.3.2. Moment Base Plate

-
-

Sample Problem 1

-
-

Design a Base Plate connection for a column PBP 400 X 140.2 subjected to the following reactions at the base;

-
    -
  • Axial Compression: 900 kN

  • -
  • Axial Tension/Uplift: 27 kN

  • -
  • -
    Shear Force (at base):
      -
    • Along major (z-z) axis: 85 kN

    • -
    • Along minor (y-y) axis: 12 kN

    • -
    -
    -
    -
  • -
  • -
    Bending Moment:
      -
    • Major (z-z) axis: 123 kN

    • -
    -
    -
    -
  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • Column: Grade of the column material is E 350 (Fe 490).

  • -
  • Anchor Bolt - Outside Column Flange: Type: End Plate Type; Diameter: M24, M30; Grade: 8.8, 12.9

  • -
  • Anchor Bolt - Inside Column Flange: Type: End Plate Type; Diameter: M20, M24; Grade: 8.8, 9.8

  • -
  • Footing: Grade of concrete is M 30

  • -
  • Base Plate: Material E 250 (Fe 410 W)A

  • -
  • Stiffener/Shear Key: Material E 250 (Fe 410 W)A

  • -
  • Bolt hole type is over-sized and the Anchor bolt is Galvanized (both, outside and inside flange)

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 510 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Members are exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.3.2.1.pdf.

-

Example_1.3.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Base Plate connection for a column HB 450 subjected to the following reactions at the base;

-
    -
  • Axial Compression: 1600 kN

  • -
  • -
    Shear Force (at base):
      -
    • Along major (z-z) axis: 80 kN

    • -
    • Along minor (y-y) axis: 23 kN

    • -
    -
    -
    -
  • -
  • -
    Bending Moment:
      -
    • Major (z-z) axis: 180 kN

    • -
    • Minor (y-y) axis: 45 kN

    • -
    -
    -
    -
  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Column: Grade of the column material is E 300 (Fe 440).

  • -
  • Anchor Bolt - Outside Column Flange: Type: End Plate Type; Diameter: M30; Grade: 10.9

  • -
  • Anchor Bolt - Inside Column Flange: Type: End Plate Type; Diameter: M20; Grade: 8.8

  • -
  • Footing: Grade of concrete is M 25

  • -
  • Base Plate: Material E 300 (Fe 440)

  • -
  • Stiffener/Shear Key: Material E 300 (Fe 440)

  • -
  • Bolt hole type is over-sized and the Anchor bolt is Galvanized (both, outside and inside flange)

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 600 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Members are not exposed to marine/corrosive environment

  • -
-
-

Download:

-

DesignReport_1.3.2.2.pdf.

-

Example_1.3.2.2.osi.

-
-
-

1.3.3. Hollow/Tubular Column Base

-
-

Sample Problem 1

-
-

Design a Base Plate connection for a square hollow column section SHS 180 x 180 x 8 subjected to the following reactions at the base;

-
    -
  • Axial Compression: 650 kN

  • -
-

Perform an optimum design by adopting the following design specifications:

-
    -
  • ** Hollow Column:** Grade of the column material is E 300 (Fe 440)

  • -
  • Anchor Bolt: Type: End Plate Type; Diameter: M20, M24, M30; Grade: 8.8, 10.9

  • -
  • Footing: Grade of concrete is M 25

  • -
  • Base Plate: Material E 250 (Fe 410 W)A

  • -
  • Stiffener Plate: Material E 250 (Fe 410 W)A

  • -
  • Bolt hole type is over-sized and the Anchor bolt is Galvanized

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 510 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Members are not exposed to corrosive environment

  • -
-
-

Download:

-

DesignReport_1.3.3.1.pdf.

-

Example_1.3.3.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Base Plate connection for a circular hollow column section CHS 355.6 x 10 subjected to the following reactions at the base;

-
    -
  • Axial Compression: 775 kN

  • -
  • -
    Shear Force (at base):
      -
    • Along major (z-z) axis: 45 kN

    • -
    • Along minor (y-y) axis: 10 kN

    • -
    -
    -
    -
  • -
-

Perform a design check by adopting the following design specifications:

-
    -
  • Column: Grade of the column material is E 300 (Fe 440).

  • -
  • Anchor Bolt: Type: End Plate Type; Diameter: M20; Grade: 8.8

  • -
  • Footing: Grade of concrete is M 35

  • -
  • Base Plate: Material E 250 (Fe 410 W)A

  • -
  • Stiffener/Shear Key: Material E 250 (Fe 410 W)A

  • -
  • Bolt hole type is over-sized and the Anchor bolt is Galvanized

  • -
  • Type of weld fabrication: Shop weld

  • -
  • The ultimate strength of the weld material is, fu = 440 MPa

  • -
  • The plate edges are machine-flame cut

  • -
  • Members are exposed to corrosive environment

  • -
-
-

Download:

-

DesignReport_1.3.3.2.pdf.

-

Example_1.3.3.2.osi.

-
-
-
-
-

2. Tension Member Design

-
-

2.1. Tension Member - Bolted to End Gusset

-
-

Sample Problem 1

-
-

Design a Tension Member with a bolted end connection to transfer a factored axial force of 235 kN.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Angle

    • -
    • Material Grade: E 250 (Fe 410 W)A

    • -
    • Connection Location: Longer leg

    • -
    • Length: 2560 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Bolted; Bolt type: Bearing bolt; Diameter: 16, 20; Grade: 4.6, 4.8

    • -
    • Bolt hole type is standard

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14, 16

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_2.1.1.pdf.

-

Example_2.1.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Tension Member with a bolted end connection to transfer a factored axial force of 330 kN.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Back to Back Angles

    • -
    • Material Grade: E 250 (Fe 410 W)A

    • -
    • Connection Location: Longer leg

    • -
    • Length: 2200 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Bolted; Bolt type: Bearing bolt; Diameter: 16, 20, 24; Grade: 6.8, 8.8

    • -
    • Bolt hole type is over-sized

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14, 16, 20

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_2.1.2.pdf.

-

Example_2.1.2.osi.

-
-
-
-

Sample Problem 3

-
-

Design a Tension Member with a bolted end connection to transfer a factored axial force of 180 kN.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Star Angles

    • -
    • Material Grade: E 165 (Fe 290)

    • -
    • Connection Location: Short leg

    • -
    • Length: 2500 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Bolted; Bolt type: Bearing bolt; Diameter: 16, 20; Grade: 5.6, 5.8, 6.8

    • -
    • Bolt hole type is standard

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14

  • -
  • The plate edges are hand flame cut

  • -
-
-

Download:

-

DesignReport_2.1.3.pdf.

-

Example_2.1.3.osi.

-
-
-
-

Sample Problem 4

-
-

Design a Tension Member with a bolted end connection to transfer a factored axial force of 240 kN.

-

Perform a design check by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Channel

    • -
    • Designation: MC 250

    • -
    • Material Grade: E 250 (Fe 410 W)A

    • -
    • Connection Location: Web

    • -
    • Length: 3200 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Bolted; Bolt type: Friction grip (HSFG); Diameter: 20; Grade: 8.8

    • -
    • Bolt hole type is standard and slip factor is 0.3

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_2.1.4.pdf.

-

Example_2.1.4.osi.

-
-
-
-

Sample Problem 5

-
-

Design a Tension Member with a bolted end connection to transfer a factored axial force of 500 kN.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Back to Back Channels

    • -
    • Material Grade: E 300 (Fe 440)

    • -
    • Connection Location: Web

    • -
    • length: 3000 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Bolted; Bolt type: Bearing bolt; Diameter: 20, 24; Grade: 8.8, 9.8, 10.9

    • -
    • Bolt hole type is over-sized

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_2.1.5.pdf.

-

Example_2.1.5.osi.

-
-
-
-

2.2. Tension Member - Welded to End Gusset

-
-

Sample Problem 1

-
-

Design a Tension Member with a welded end connection to transfer a factored axial force of 235 kN.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Angle

    • -
    • Material Grade: E 250 (Fe 410 W)A

    • -
    • Connection Location: Longer leg

    • -
    • Length: 2560 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Welded; Type of weld fabrication: Field weld

    • -
    • The ultimate strength of the weld material is, fu = 450 MPa

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14, 16

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_2.2.1.pdf.

-

Example_2.2.1.osi.

-
-
-
-

Sample Problem 2

-
-

Design a Tension Member with bolted end connection to transfer a factored axial force of 330 kN.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Back to Back Angles

    • -
    • Material Grade: E 250 (Fe 410 W)A

    • -
    • Connection Location: Longer leg

    • -
    • Length: 2200 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Welded; Type of weld fabrication: Shop weld

    • -
    • The ultimate strength of the weld material is, fu = 410 MPa

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14, 16, 20

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_2.2.2.pdf.

-

Example_2.2.2.osi.

-
-
-
-

Sample Problem 3

-
-

Design a Tension Member with bolted end connection to transfer a factored axial force of 180 kN.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Star Angles

    • -
    • Material Grade: E 165 (Fe 290)

    • -
    • Connection Location: Short leg

    • -
    • Length: 2500 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Welded; Type of weld fabrication: Field weld

    • -
    • The ultimate strength of the weld material is, fu = 410 MPa

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14

  • -
  • The plate edges are hand flame cut

  • -
-
-

Download:

-

DesignReport_2.2.3.pdf.

-

Example_2.2.3.osi.

-
-
-
-

Sample Problem 4

-
-

Design a Tension Member with bolted end connection to transfer a factored axial force of 240 kN.

-

Perform a design check by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Channel

    • -
    • Designation: MC 250

    • -
    • Material Grade: E 250 (Fe 410 W)A

    • -
    • Connection Location: Web

    • -
    • Length: 3200 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Welded; Type of weld fabrication: Field weld

    • -
    • The ultimate strength of the weld material is, fu = 410 MPa

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 14

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_2.2.4.pdf.

-

Example_2.2.4.osi.

-
-
-
-

Sample Problem 5

-
-

Design a Tension Member with bolted end connection to transfer a factored axial force of 500 kN.

-

Perform an optimum design by adopting the following design specifications:

-
    -
  • -
    Section:
      -
    • Profile: Back to Back Channels

    • -
    • Material Grade: E 300 (Fe 440)

    • -
    • Connection Location: Web

    • -
    • length: 3000 mm

    • -
    -
    -
    -
  • -
  • -
    End Connection:
      -
    • Connector: Type: Welded; Type of weld fabrication: Shop weld

    • -
    • The ultimate strength of the weld material is, fu = 440 MPa

    • -
    -
    -
    -
  • -
  • Gusset Plate: Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20

  • -
  • The plate edges are machine-flame cut

  • -
-
-

Download:

-

DesignReport_2.2.5.pdf.

-

Example_2.2.5.osi.

-
-
-

Other Resources

-
-
    -
  • How to use Osdag? Learn through Osdag’s audio-video Tutorials

  • -
  • We are on YouTube as well. Like and Subscribe our channel to keep up-to-date with Osdag

  • -
  • Comments, questions, suggestions? Check the forum Discussion

  • -
  • Or, ask us a query directly through the FOSSEE Forum

  • -
-
-
-
-

Copyright © 2017 Osdag. All rights reserved.

-
-
-
-
-
-
- - -
-
-
-
- -
-
- - - - \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/objects.inv b/ResourceFiles/html_page/_build/html/objects.inv deleted file mode 100644 index 052d8622a..000000000 Binary files a/ResourceFiles/html_page/_build/html/objects.inv and /dev/null differ diff --git a/ResourceFiles/html_page/_build/html/search.html b/ResourceFiles/html_page/_build/html/search.html deleted file mode 100644 index 15c83d8d3..000000000 --- a/ResourceFiles/html_page/_build/html/search.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - Search — Main Home | Help_Design_Examples 2017.06.0.2 documentation - - - - - - - - - - - - - - - - - - - -
-
-
-
- -

Search

-
- -

- Please activate JavaScript to enable the search - functionality. -

-
-

- Searching for multiple words only shows matches that contain - all words. -

-
- - - -
- -
- -
- -
-
-
-
- -
-
- - - - \ No newline at end of file diff --git a/ResourceFiles/html_page/_build/html/searchindex.js b/ResourceFiles/html_page/_build/html/searchindex.js deleted file mode 100644 index f3ee8066c..000000000 --- a/ResourceFiles/html_page/_build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({docnames:["index"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,sphinx:56},filenames:["index.rst"],objects:{},objnames:{},objtypes:{},terms:{"100kn":[],"10mm":0,"115kn":[],"120kn":0,"12kn":0,"12knm":0,"12mm":0,"140x140x24":0,"14mm":0,"150knm":[],"15kn":0,"15knm":0,"15mm":0,"160x80x15":0,"16mm":0,"175knm":[],"20kn":0,"20mm":0,"220x110x29":0,"22mm":0,"240x240x60":0,"24mm":0,"250x150x39":0,"25kn":0,"25knm":0,"26mm":0,"280x280x61":0,"300x180":0,"300x300x96":0,"350x170x50":0,"35kn":0,"406x178x67":[],"457x191x82":[],"50kn":0,"50knm":0,"5mm":0,"6mm":0,"75kn":[],"75knm":0,"8mm":0,"class":0,"new":0,"try":0,One:0,The:0,Use:0,act:0,all:0,anjali:0,appropri:0,asasasadesign:0,assum:0,avail:0,axial:0,base:0,beam_bolted1:[],bear:0,bend:0,beta:0,between:0,blast:0,bolt:0,bombai:0,both:0,built:0,can:0,cfbf:0,cfbw:0,cjp:0,comment:0,contact:0,contain:0,copyright:0,corros:0,cross:0,custom:0,customis:0,cut:0,cwbf:0,cwbw:0,design:0,designreport_1:0,detail:0,develop:0,diamet:0,download:0,e250:0,each:0,edg:0,environ:0,etc:0,exampl:0,example_1:0,extend:0,extent:0,extern:0,factor:0,fe410:0,feedback:0,field:0,file:0,fillet:0,flame:0,flang:0,flush:0,follow:0,forc:0,format:0,foss:0,four:0,free:0,friction:0,gap:0,girp:0,grade:0,grip:0,groov:0,hand:0,head:[],hole:0,iit:0,index:0,indian:0,input:0,insid:0,knm:0,load:0,m12:0,m16:0,m20:0,m24:0,m30:0,machin:0,materi:0,member:0,metal:0,modul:0,mpa:0,non:0,npb:0,one:0,open:0,opencascad:0,option:0,osi:0,osi_:[],other:0,outsid:0,overs:0,page:0,pbp:0,pdf:0,pdf_:[],per:0,platform:0,pre:0,prefer:0,prepar:0,pretens:0,previou:0,primari:0,primarili:0,problem:0,properti:0,pyqt:0,python:0,pythonocc:0,question:0,relev:0,reserv:0,respect:0,revers:0,right:0,sampl:0,sand:0,search:0,secondari:0,section:0,see:0,shop:0,side:0,size:0,slip:0,softwar:0,sourc:0,splice:0,sqlite:0,standard:0,steel:0,structur:0,suggest:0,surfac:0,take:0,team:0,tension:0,thi:0,thick:0,tool:0,top:0,transfer:0,treat:0,treatment:0,type:0,upon:0,use:0,using:0,version:0,vertic:0,wai:0,web:0,weld:0,wpb:0,x254:0},titles:["Welcome to Osdag help!"],titleterms:{angl:0,beam:0,cleat:0,column:0,connect:0,cover:0,end:0,fin:0,help:0,homepag:0,indic:0,moment:0,osdag:0,plate:0,seat:0,shear:0,tabl:0,truss:0,welcom:0}}) \ No newline at end of file diff --git a/ResourceFiles/html_page/conf.py b/ResourceFiles/html_page/conf.py deleted file mode 100644 index 2f7ef72f9..000000000 --- a/ResourceFiles/html_page/conf.py +++ /dev/null @@ -1,188 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = u'Main Home | Help_Design_Examples' -copyright = u'2017, Osdag Team' -author = u'Osdag Team' - -# The short X.Y version -version = u'0.2' -# The full version, including alpha/beta/rc tags -release = u'2021.01' - - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.mathjax'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path . -exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -#html_theme = 'alabaster' -html_theme = "classic" -html_theme_options = { -"rightsidebar": "false", -"sidebarwidth": "300", -"stickysidebar": "True", -"sidebarbgcolor": "YellowGreen ", -"relbarbgcolor": " YellowGreen", -"sidebartextcolor":"black", -"sidebarlinkcolor":"black" -#"bgcolor":"#91B014" -} -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'Help_Design_Examplesdocdoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'Help_Design_Examplesdoc.tex', u'Osdag\\_design\\_examples Documentation', - u'Osdag Team', 'manual'), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'Help_Design_Examplesdoc', u'Help_Design_Examplesdoc Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'Help_Design_Examplesdoc', u'Help_Design_Examplesdoc Documentation', - author, 'Help_Design_Examplesdoc', 'One line description of project.', - 'Miscellaneous'), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project -epub_author = author -epub_publisher = author -epub_copyright = copyright - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] \ No newline at end of file diff --git a/ResourceFiles/html_page/index.rst b/ResourceFiles/html_page/index.rst deleted file mode 100644 index c681b9854..000000000 --- a/ResourceFiles/html_page/index.rst +++ /dev/null @@ -1,2611 +0,0 @@ -.. Help_Design_Examples documentation master file, created by - sphinx-quickstart on Mon Jun 12 15:06:32 2017. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -.. image:: website_header.png - :target: http://osdag.fossee.in/ - - - -Welcome to Osdag help! -================================================ - -Osdag is a cross-platform free and open-source software for the design and detailing of steel structures, following relevant Indian Standards. Osdag is primarily built upon Python and other Python-based FOSS tools, such as, PyQt, OpenCascade, PythonOCC, SQLite, etc. It is developed by the Osdag team at IIT Bombay. - | - -The OSI file of a sample design can be loaded through the ``File -> Load input`` option of the appropriate module. - -.. note:: The examples highlighted inside a box are from the **Release 2018-06-21** and older versions of Osdag. Please use the examples with the appropriate version of Osdag. -| - - -************************** -Osdag Homepage_. -************************** - -*********************** -1. **Connection** -*********************** - -1.1. *Shear Connection* -####################### - -1.1.1. Fin Plate -**************************** - -1.1.1.1 Column Flange-Beam Web (CFBW) connectivity - - **Sample Problem 1** - - Design a *Fin Plate* connection for a beam *MB 350* connected to a column *HB 450* to transfer a factored shear force of **180 kN** and an axial force of **50 kN**. - The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Diameter: 12, 16, 20, 24, 30; Grade: 4.6, 4.8, 5.6, 6.8, 8.8 - - **Plate:** Material E 350 (Fe 490); Thickness (mm): 10, 12, 16, 18, 20 - - Bolt hole type is standard and slip factor is 0.3 - - The plate edges are machine-flame cut - - Gap between the members is 15 mm - - **Download:** - - DesignReport_1.1.1.1.1.pdf_. - - Example_1.1.1.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Fin Plate* connection for a beam *WB 300* connected to a column *PBP 300 X 88.48* to transfer a factored shear force of **175 kN** and an axial force of **35 kN**. - The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20; Grade: 6.8 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14 - - Bolt hole type is standard - - The plate edges are hand flame cut - - Gap between the members is 10 mm - - **Download:** - - DesignReport_1.1.1.1.2.pdf_. - - Example_1.1.1.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.1.1.2. Column Web-Beam Web (CWBW) connectivity - - **Sample Problem 1** - - Design a *Fin Plate* connection for a beam *NPB 350 X 250 X 79.18* connected to a column *PBP 400 X 230.9* to transfer a factored shear force of **225 kN** and an axial force of **100 kN**. - The grade of the beam and the column material is **E 350 (Fe 410 W)A** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 12, 16, 20, 24 ; Grade: 4.6, 8.8, 10.9 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 12, 14, 16, 18, 20, 22, 25 - - Bolt hole type is over-sized - - The ultimate strength of the weld material is, f\ :sub:`u` = 510 MPa - - The plate edges are machine-flame cut - - Gap between the members is 12 mm - - **Download:** - - DesignReport_1.1.1.2.1.pdf_. - - Example_1.1.1.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Fin Plate* connection for a beam *WPB 300 X 300 X 100.85* connected to a column *UC 356 X 368 X 129* to transfer a factored shear force of **140 kN**. - The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16; Grade: 4.6 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16 - - Bolt hole type is standard - - The plate edges are machine-flame cut - - Gap between the members is 10 mm - - **Download:** - - DesignReport_1.1.1.2.2.pdf_. - - Example_1.1.1.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.1.1.3. Beam-Beam (BB) connectivity - - **Sample Problem 1** - - Design a *Fin Plate* connection for a primary beam *WB 400* connected to a secondary beam *MB 300* to transfer a factored shear force of **160 kN** and an axial force of **20 kN**. - The grade of the primary and secondary beam material is **E 300 (Fe 440)** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Diameter: 16, 20, 24 ; Grade: 8.8, 10.9 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20, 22 - - Bolt hole type is standard - - Surface treatment: Sand blast (slip factor = 0.48) - - The plate edges are machine-flame cut - - Gap between the members is 5 mm - - **Download:** - - DesignReport_1.1.1.3.1.pdf_. - - Example_1.1.1.3.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Fin Plate* connection for a primary beam *UB 305 x 102 x 33* connected to a secondary beam *MB 300* to transfer a factored shear force of **100 kN**. - The grade of the primary and secondary beam material is **E 250 (Fe 410 W)A** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16; Grade: 5.8 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14 - - Bolt hole type is over-sized - - The ultimate strength of the weld material is, f\ :sub:`u` = 450 MPa - - The plate edges are hand flame cut - - Gap between the members is 10 mm - - **Download:** - - DesignReport_1.1.1.3.2.pdf_. - - Example_1.1.1.3.2.osi_. - -.. note:: Example for **Release 2018-06-21** and lower versions - Fin Plate - - 1.1.1.1 Column Flange-Beam Web (CFBW) connectivity - - **Sample Problem 1** - - Design a fin plate connection between a beam MB 500 and a column UC 305 x 305 x 97 for transferring a vertical (factored) shear force of 140 kN. Use M24 Friction grip bolts of grade 8.8. Try 12mm thick fin plate with weld thickness of 12mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, weld type as shop weld and type of edge as hand flame cut. - - **Download:** - - Design_Report_1.1.1.1.1.pdf_. - - Design_Example_1.1.1.1.1.osi_. - - - **Sample Problem 2** - - Design a fin plate connection between a beam MB 350 and a column SC 200 for transferring a vertical (factored) shear force of 150 kN. Use M20 bearing bolts of grade 4.6. Try 12mm thick fin plate with weld thickness of 8mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume weld type as shop weld and type of edge as machine flame cut. - - **Download:** - - Design_Report_1.1.1.1.2.pdf_. - - Design_Example_1.1.1.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -.. note:: Example for **Release 2018-06-21** and lower versions - Fin Plate - - 1.1.1.2. Column Web-Beam Web (CWBW) connectivity - - **Sample Problem 1** - - Design a fin plate connection between a beam UB 356 x 171 x 45 and a column PBP 300 x 180 for transferring a vertical (factored) shear force of 120 kN. Use M16 Friction grip bolts of grade 8.8. Try 8mm thick fin plate with weld thickness of 8mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, slip factor of 0.25, type of weld as field weld and edge as hand flame cut. - - **Download:** - - Design_Report_1.1.1.2.1.pdf_. - - Design_Example_1.1.1.2.1.osi_. - - - **Sample Problem 2** - - Design a fin plate connection between a beam LB 300 and a column SC 250 for transferring a vertical (factored) shear force of 135 kN. Use M24 bearing bolts of grade 4.8. Try 10mm thick fin plate with weld thickness of 8mm. Take Fe410 grade steel fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, type of weld as shop weld and edge as hand flame cut and environment as corrosive. - - **Download:** - - Design_Report_1.1.1.2.2.pdf_. - - Design_Example_1.1.1.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -.. note:: Example for **Release 2018-06-21** and lower versions - Fin Plate - - 1.1.1.3. Beam-Beam (BB) connectivity - - *Sample Problem 1* - - Design a fin plate connection between a primary beam MB 350 and a secondary beam NPB 270 x 135 x 36.1 for transferring a vertical (factored) shear force of 110 kN. Use M20 Friction grip bolts of grade 10.9. Try 10mm thick fin plate with weld thickness of 8mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.52, bolt hole type oversized and type of edge as machine flame cut. - - **Download:** - - Design_Report_1.1.1.3.1.pdf_. - - Design_Example_1.1.1.3.1.osi_. - - - **Sample Problem 2** - - Design a fin plate connection between a primary beam WPB 450 x 300 x 99.7 and a secondary beam UB 356 x 171 x 67 for transferring a vertical (factored) shear force of 220 kN. Use M24 Friction grip bolts of grade 10.9. Try 14mm thick fin plate with weld thickness of 12mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.48, bolt hole type standard, weld type as shop weld and type of edge as machine flame cut. - - **Download:** - - Design_Report_1.1.1.3.2.pdf_. - - Design_Example_1.1.1.3.2.osi_. - -1.1.2. End Plate -**************************** - -1.1.2.1. Column Flange-Beam Web (CFBW) connectivity - - **Sample Problem 1** - - Design an *End Plate* connection for a beam *LB 400* connected to a column *PBP 300 X 109.54* to transfer a factored shear force of **240 kN** and an axial force of **125 kN**. - The grade of the beam and the column material are **E 250 (Fe 410 W)A** and **E 350 (Fe 490)** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16, 20, 24, 30; Grade: 4.8, 5.6, 6.8, 9.8 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14, 16, 18, 20, 22, 25, 28 - - Bolt hole type is standard - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_1.1.2.1.1.pdf_. - - Example_1.1.2.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design an *End Plate* connection for a beam *UB 356 x 127 x 39* connected to a column *UB 254 x 254 x 89* to transfer a factored shear force of **250 kN** and an axial force of **80 kN**. - The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Diameter: 24; Grade: 8.8 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 18 - - Bolt hole type is standard and slip factor is 0.25 - - The plate edges are hand flame cut - - **Download:** - - DesignReport_1.1.2.1.2.pdf_. - - Example_1.1.2.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.1.2.2. Column Web-Beam Web (CWBW) connectivity - - **Sample Problem 1** - - Design an *End Plate* connection for a beam *WB 300* connected to a column *HB 350* to transfer a factored shear force of **220 kN** and an axial force of **30 kN**. - The grade of the beam and the column material is **E 250 (Fe 410 W)A**. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16, 20, 24; Grade: 4.6, 4.8, 5.6, 5.8 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14, 16, 20 - - Bolt hole type is over-sized - - The ultimate strength of the weld material is, f\ :sub:`u` = 500 MPa - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_1.1.2.2.1.pdf_. - - Example_1.1.2.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design an *End Plate* connection for a beam *MB 400* connected to a column *HB 350* to transfer a factored shear force of **275 kN**. - The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20; Grade: 9.8 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness(mm): 20 - - Bolt hole type is standard - - The plate edges are are machine-flame cut - - **Download:** - - DesignReport_1.1.2.2.2.pdf_. - - Example_1.1.2.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.1.2.3. Beam-Beam (BB) connectivity - - **Sample Problem 1** - - Design an *End Plate* connection for a primary beam *WB 400* connected to a secondary beam *MB 300* to transfer a factored shear force of **160 kN** and an axial force of **20 kN**. - The grade of the primary and secondary beam material is **E 300 (Fe 440)** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Diameter: 16, 20, 24 ; Grade: 8.8, 10.9 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20, 22 - - Bolt hole type is standard - - Surface treatment: Sand blast (slip factor = 0.48) - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_1.1.2.3.1.pdf_. - - Example_1.1.2.3.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design an *End Plate* connection for a primary beam *UB 305 x 102 x 33* connected to a secondary beam *MB 300* to transfer a factored shear force of **100 kN**. - The grade of the primary and secondary beam material is **E 250 (Fe 410 W)A** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16; Grade: 5.8 - - **Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14 - - Bolt hole type is over-sized - - The ultimate strength of the weld material is, f\ :sub:`u` = 450 MPa - - The plate edges are hand flame cut - - **Download:** - - DesignReport_1.1.2.3.2.pdf_. - - Example_1.1.2.3.2.osi_. - -.. note:: Example for **Release 2018-06-21** and lower versions - End Plate - - 1.1.2.1. Column Flange-Beam Web (CFBW) connectivity - - **Sample Problem 1** - - Design an end plate connection between a beam MB 350 and a column SC 250 for transferring a vertical (factored) shear force of 140 kN. Use M20 bearing bolts of grade 4.6. Try 10mm thick end plate with weld thickness of 6mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, weld type shop weld and type of edge as hand flame cut. - **Download:** - - Design_Report_1.1.2.1.1.pdf_. - - Design_Example_1.1.2.1.1.osi_. - - - **Sample Problem 2** - - Design an end plate connection between a beam MB 300 and a column UC 254 x254 x 107 for transferring a vertical (factored) shear force of 195 kN. Use M16 Friction grip bolts of grade 8.8. Try 12mm thick end plate with weld thickness of 10mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, weld type shop weld and type of edge as machine flame cut. - - **Download:** - - Design_Report_1.1.2.1.2.pdf_. - - Design_Example_1.1.2.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -.. note:: Example for **Release 2018-06-21** and lower versions - End Plate - - 1.1.2.2. Column Web-Beam Web (CWBW) connectivity - - **Sample Problem 1** - - Design an end plate connection between a beam UB 356 x 171 x 45 and a column PBP 300 x 180 for transferring a vertical (factored) shear force of 120 kN. Use M16 Friction grip bolts of grade 10.9. Try 12mm thick end plate with weld thickness of 6mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, slip factor of 0.25, type of weld as field weld and edge type as hand flame cut. - - **Download:** - - Design_Report_1.1.2.2.1.pdf_. - - Design_Example_1.1.2.2.1.osi_. - - - **Sample Problem 2** - - Design an end plate connection between a beam LB 300 and a column SC 250 for transferring a vertical (factored) shear force of 135 kN. Use M12 bearing bolts of grade 4.8. Try 10mm thick end plate with weld thickness of 10mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, type of weld as shop weld and edge type as hand flame cut. - - **Download:** - - Design_Report_1.1.2.2.2.pdf_. - - Design_Example_1.1.2.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -.. note:: Example for **Release 2018-06-21** and lower versions - End Plate - - 1.1.2.3. Beam-Beam (BB) connectivity - - **Sample Problem 1** - - Design an end plate connection between a primary beam MB 500 and a secondary beam MB 400 for transferring a vertical (factored) shear force of 160 kN. Use M20 Friction grip bolts of grade 8.8. Try 16mm thick end plate with weld thickness of 8mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.2, bolt hole type as oversized, weld type shop weld and type of edge as machine flame cut. - - **Download:** - - Design_Report_1.1.2.3.1.pdf_. - - Design_Example_1.1.2.3.1.osi_. - - - **Sample Problem 2** - - Design an end plate connection between a primary beam WPB 450 x 300 x 99.7 and a secondary beam UB 356 x 171 x 67 for transferring a vertical (factored) shear force of 220 kN. Use M24 Friction grip bolts of grade 10.9. Try 14mm thick end plate with weld thickness of 12mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.48, bolt hole type as standard, weld type shop weld and type of edge as machine flame cut. - - **Download:** - - Design_Report_1.1.2.3.2.pdf_. - - Design_Example_1.1.2.3.2.osi_. - - -1.1.3. Cleat Angle -******************************* - -1.1.3.1. Column Flange-Beam Web (CFBW) connectivity - - **Sample Problem 1** - - Design a *Cleat Angle* connection for a beam *MB 300* connected to a column *HB 250* to transfer a factored shear force of **130 kN**. - The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16, 20; Grade: 4.6, 4.8 - - **Angle:** Material E 250 (Fe 410 W)A; Designation: 90 x 90 x 10, 90 x 90 x 12, 100 x 100 x 6, 100 x 100 x 8 - - Bolt hole type is standard - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_1.1.3.1.1.pdf_. - - Example_1.1.3.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Cleat Angle* connection for a beam *MB 450* connected to a column *UC 305 x 305 x 118* to transfer a factored shear force of **240 kN**. - The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Diameter: 20; Grade: 12.9 - - **Cleat Angle:** Material E 250 (Fe 410 W)A; Designation: 120 x 120 x 8 - - Bolt hole type is standard - - Surface treatment: Clean mill scale (slip factor = 0.33) - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_1.1.3.1.2.pdf_. - - Example_1.1.3.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.1.3.2. Column Web-Beam Web (CWBW) connectivity - - **Sample Problem 1** - - Design a *Cleat Angle* connection for a beam *WPB 250 X 250 X 85.4* connected to a column *PBP 300 X 95* to transfer a factored shear force of **170 kN**. - The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16, 20, 24; Grade: 6.8, 8.8, 98 - - **Angle:** Material E 250 (Fe 410 W)A; Designation: All the sections available with the Osdag database - - Bolt hole type is standard - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_1.1.3.2.1.pdf_. - - Example_1.1.3.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Cleat Angle* connection for a beam *MB 450* connected to a column *UC 305 x 305 x 118* to transfer a factored shear force of **240 kN**. - The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Diameter: 20; Grade: 12.9 - - **Cleat Angle:** Material E 250 (Fe 410 W)A; Designation: 80 x 80 x 8 - - Bolt hole type is standard - - Surface treatment: Clean mill scale (slip factor = 0.33) - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_1.1.3.2.2.pdf_. - - Example_1.1.3.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.1.3.3. Beam-Beam (BB) connectivity - - **Sample Problem 1** - - Design a *Cleat Angle* connection for a primary beam *WB 400* connected to a secondary beam *MB 300* to transfer a factored shear force of **160 kN**. - The grade of the primary and secondary beam material is **E 300 (Fe 440)** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Diameter: 16, 20, 24 ; Grade: 8.8, 12.9 - - **Cleat Angle:** Material E 165 (Fe 290); Designation: 80 x 80 x 8, 90 x 90 x 6, 90 x 90 x 8, 90 x 90 x 10, 90 x 90 x 12, 100 x 100 x 8, 100 x 100 x 10, 110 x 110 x 10 - - Bolt hole type is standard - - Surface treatment: Sand blast (slip factor = 0.48) - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_1.1.3.3.1.pdf_. - - Example_1.1.3.3.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Cleat Angle* connection for a primary beam *UB 305 x 102 x 33* connected to a secondary beam *MB 300* to transfer a factored shear force of **100 kN**. - The grade of the primary and secondary beam material is **E 250 (Fe 410 W)A** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16; Grade: 5.8 - - **Cleat Angle:** Material E 250 (Fe 410 W)A; Designation: 130 x 130 x 10 - - Bolt hole type is over-sized - - The plate edges are hand flame cut - - **Download:** - - DesignReport_1.1.3.3.2.pdf_. - - Example_1.1.3.3.2.osi_. - -.. note:: Examples for **Release 2018-06-21** and lower versions - Cleat Angle - - 1.1.3.1. Column Flange-Beam Web (CFBW) connectivity - - **Sample Problem 1** - - Design a cleat angle connection between a beam MB 400 and a column SC 250 for transferring a vertical (factored) shear force of 140 kN. Use M20 Friction grip bolts of grade 8.8. Try cleat angle of size 90 x 90 x 12. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.48, bolt hole type as standard and type of edge as hand flame cut. - **Download:** - - Design_Report_1.1.3.1.1.pdf_. - - Design_Example_1.1.3.1.1.osi_. - - - **Sample Problem 2** - - Design a cleat angle connection between a beam MB 350 and a column HB 300 for transferring a vertical (factored) shear force of 170 kN. Use M16 bearing bolts of grade 4.6. Try cleat angle of size 100 x 100 x 12. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, type of edge as machine flame cut and environment as corrosive. - - **Download:** - - Design_Report_1.1.3.1.2.pdf_. - - Design_Example_1.1.3.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -.. note:: Examples for **Release 2018-06-21** and lower versions - Cleat Angle - - 1.1.3.2. Column Web-Beam Web (CWBW) connectivity - - **Sample Problem 1** - - Design a cleat angle connection between a beam MB 350 and a column UC 305 x 305 x 97 for transferring a vertical (factored) shear force of 120.5 kN. Use M20 Friction grip bolts of grade 8.8. Try cleat angle of size 110 x 110 x 16. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, slip factor of 0.2, type of edge as hand flame cut and gap between column and beam as 5mm. - - **Download:** - - Design_Report_1.1.3.2.1.pdf_. - - Design_Example_1.1.3.2.1.osi_. - - - **Sample Problem 2** - - Design a cleat angle connection between a beam MB 200 and a column UC 305 x 305 x 118 for transferring a vertical (factored) shear force of 80 kN. Use M12 bearing bolts of grade 6.8. Try cleat angle of size 110 x 110 x 16. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, type of edge as hand flame cut and environment as corrosive. - - **Download:** - - Design_Report_1.1.3.2.2.pdf_. - - Design_Example_1.1.3.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -.. note:: Examples for **Release 2018-06-21** and lower versions - Cleat Angle - - 1.1.3.3. Beam-Beam (BB) connectivity - - **Sample Problem 1** - - Design a cleat angle connection between a primary beam WB 450 and a secondary beam MB 400 for transferring a vertical (factored) shear force of 145 kN. Use M24 bearing bolts of grade 4.6. Try cleat angle of size 100 100 x 10. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard and type of edge as hand flame cut. - - **Download:** - - Design_Report_1.1.3.3.1.pdf_. - - Design_Example_1.1.3.3.1.osi_. - - - **Sample Problem 2** - - Design a cleat angle connection between a primary beam WPB 280x280x61.2 and a secondary beam NPB 220x110x29.4 for transferring a vertical (factored) shear force of 100 kN. Use M20 Friction grip bolts of grade 10.9. Try cleat angle of size 100 100 x 8. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as oversized, slip factor of 0.52, type of edge as machine flame cut and gap between column and beam as 15mm. - - **Download:** - - Design_Report_1.1.3.3.2.pdf_. - - Design_Example_1.1.3.3.2.osi_. - - -1.1.4. Seated Angle -******************************* - -1.1.4.1. Column Flange-Beam Flange (CFBF) connectivity - - **Sample Problem 1** - - Design a *Seated Angle* connection for a beam *WB 400* connected to a column *PBP 360 X 152.2* to transfer a factored shear force of **210 kN**. - The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24, 30; Grade: 4.6, 4.8, 5.6, 5.8, 6.8, 8.8, 9.8 - - **Seated Angle:** Material E 300 (Fe 440); Designation: All the sections available with the Osdag database - - **Top Angle:** Material E 300 (Fe 440); Designation: All the sections available with the Osdag database - - Bolt hole type is standard - - The plate edges are machine-flame cut - - Gap between the members is 10 mm - - **Download:** - - DesignReport_1.1.4.1.1.pdf_. - - Example_1.1.4.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Seated Angle* connection for a beam *MB 500* connected to a column *UC 305 x 305 x 118* to transfer a factored shear force of **185 kN**. - The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Diameter: 20; Grade: 12.9 - - **Seated Angle:** Material E 250 (Fe 410 W)A; Designation: 150 x 150 x 10 - - **Top Angle:** Material E 250 (Fe 410 W)A; Designation: 150 x 150 x 10 - - Bolt hole type is standard - - Surface treatment: Blasted with short or grit, loose rust removed (slip factor = 0.5) - - The plate edges are machine-flame cut - - Gap between the members is 10 mm - - **Download:** - - DesignReport_1.1.4.1.2.pdf_. - - Example_1.1.4.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.1.4.2. Column Web-Beam Flange (CWBF) connectivity - - **Sample Problem 1** - - Design a *Seated Angle* connection for a beam *MB 400* connected to a column *HB 450* to transfer a factored shear force of **230 kN**. - The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24, 30; Grade: 4.6, 4.8, 5.6, 5.8, 6.8, 8.8, 9.8 - - **Seated Angle:** Material E 250 (Fe 410 W)A; Designation: All the sections available with the Osdag database - - **Top Angle:** Material E 250 (Fe 410 W)A; Designation: All the sections available with the Osdag database - - Bolt hole type is standard - - The plate edges are machine-flame cut - - Gap between the members is 10 mm - - **Download:** - - DesignReport_1.1.4.2.1.pdf_. - - Example_1.1.4.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Seated Angle* connection for a beam *LB 325* connected to a column *UC 203 x 203 x 52* to transfer a factored shear force of **90 kN**. - The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16; Grade: 8.8 - - **Seated Angle:** Material E 250 (Fe 410 W)A; Designation: 90 x 90 x 10 - - **Top Angle:** Material E 250 (Fe 410 W)A; Designation: 80 x 80 x 10 - - Bolt hole type is standard - - The plate edges are machine-flame cut - - Gap between the members is 10 mm - - **Download:** - - DesignReport_1.1.4.2.2.pdf_. - - Example_1.1.4.2.2.osi_. - -.. note:: Examples for **Release 2018-06-21** and lower versions - Seated Angle - - 1.1.4.1. Column Flange-Beam Flange (CFBF) connectivity - - **Sample Problem 1** - - Design a seated angle connection between a beam MB 300 and a column UC 203 x 203 x 86 for transferring a vertical (factored) shear force of 100 kN. Use M20 Friction grip bolts of grade 10.9. Try a top angle of size 150 150 x 10 and seated angle of size 150 150 x 15. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume slip factor of 0.55, bolt fu = 940 MPa and type of edge as hand flame cut. - - **Download:** - - Design_Report_1.1.4.1.1.pdf_. - - Design_Example_1.1.4.1.1.osi_. - - - **Sample Problem 2** - - Design a seated angle connection between a beam MB 200 and a column SC 140 for transferring a vertical (factored) shear force of 140 kN. Use M12 bearing bolts of grade 6.8. Try a top angle of size 90 90 x 8 and seated angle of size 110 110 x 16. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume type of edge as hand flame cut and environment as corrosive. - - **Download:** - - Design_Report_1.1.4.1.2.pdf_. - - Design_Example_1.1.4.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -.. note:: Examples for **Release 2018-06-21** and lower versions - Seated Angle - - 1.1.4.2. Column Web-Beam Flange (CWBF) connectivity - - **Sample Problem 1** - - Design a seated angle connection between a beam NPB 250x150x39.8 and a column PBP 300x180 for transferring a vertical (factored) shear force of 80 kN. Use M16 bearing bolts of grade 5.8. Try a top angle of size 90 90 x 10 and seated angle of size 150 150 x 15. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume type of edge as machine flame cut. - - **Download:** - - Design_Report_1.1.4.2.1.pdf_. - - Design_Example_1.1.4.2.1.osi_. - - - **Sample Problem 2** - - Design a seated angle connection between a beam WPB 140x140x24.7 and a column HB 200 for transferring a vertical (factored) shear force of 140 kN. Use M12 bearing bolts of grade 5.8. Try a top angle of size 90 90 x 10 and seated angle of size 150 150 x 15. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as Oversized, type of edge as machine flame cut and gap between column and beam as 5mm. - - **Download:** - - Design_Report_1.1.4.2.2.pdf_. - - Design_Example_1.1.4.2.2.osi_. - - -1.2. *Moment Connection* -####################### -1.2.1. Beam-to-Beam Splice Connection -******************* -1.2.1.1. Cover Plate Bolted ---------------------------------- - - **Sample Problem 1** - - Design a *Beam-Beam Cover Plate Splice* connection for a beam *UB 610 x 305 x 179*. The grade of the beam material is **E 300 (Fe 440)**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 300 kNm (reversible) - - - **Shear Force**: 160 kN - - - **Axial Force**: 40 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24, 30, 36; Grade: 6.8, 8.8, 9.8 - - **Flange Splice Plate:** Material E 250 (Fe 410 W)A; Preference: Outside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - **Web Splice Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - Bolt hole type is standard - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.1.1.1.pdf_. - - Example_1.2.1.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Beam Cover Plate Splice* connection for a beam *WPB 900 x 300 x 198.01*. The grade of the beam material is **E 350 (Fe 490)**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 395 kNm (reversible) - - - **Shear Force**: 110 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 24; Grade: 8.8 - - **Flange Splice Plate:** Material E 300 (Fe 440); Preference: Outside + Inside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - **Web Splice Plate:** Material E 300 (Fe 440); Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - Bolt hole type is over-sized - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.1.1.2.pdf_. - - Example_1.2.1.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -.. note:: Example for **Release 2018-06-21** and lower versions - Cover Plate Bolted Connection - - **Sample Problem 1** - - Design a bolted cover plate splice connection for beam MB 450 to transfer a factored external moment of 140 kNm, factored shear of 110 kN and factored axial force of 40 kN. Use M20 Friction grip bolts of grade 8.8. Try 20mm thick flange splice plate and 10mm thick web splice plate. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as standard, edge as hand flame cut, gap between the beams as 5mm and surface of the metal to be treated with sand blast. - - **Download:** - - Design_Report_1.2.1.1.1.1.pdf_. - - Design_Example_1.2.1.1.1.1.osi_. - - - **Sample Problem 2** - - Design a bolted cover plate splice connection for beam NPB 350 x 250 x 79.2 to transfer a factored external moment and shear force of 225 kNm ad 55 kN. Use M24 Friction grip bolts of grade 10.9. Try 14mm thick flange splice plate and 6mm thick web splice plate. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume bolt hole type as oversized, edge as machine-flame cut, slip factor as 0.33 and gap between the beams as 3mm. - - **Download:** - - Design_Report_1.2.1.1.1.2.pdf_. - - Design_Example_1.2.1.1.1.2.osi_. - -1.2.1.2. End Plate --------------------------------- -1.2.1.2.1. Coplanar Tension-Compression Flange - -1.2.1.2.1.1. Flushed - Reversible Moment - - **Sample Problem 1** - - Design a *Beam-Beam End Plate Splice* connection for a beam *WB 500*. The grade of the beam material is **E 250 (Fe 410 W)A**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 250 kNm (reversible) - - - **Shear Force**: 120 kN - - - **Axial Force**: 35 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24, 30; Grade: 6.8, 8.8 - - **End Plate:** Material E 300 (Fe 440); Thickness (mm): 20, 22, 25, 28, 32 - - Bolt hole type is standard - - The ultimate strength of the weld material is, f\ :sub:`u` = 500 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.1.2.1.1.1.pdf_. - - Example_1.2.1.2.1.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Beam End Plate Splice* connection for a beam *MB 400*. The grade of the beam material is **E 250 (Fe 410 W)A**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 100 kNm (reversible) - - - **Shear Force**: 40 kN - - - **Axial Force**: 20 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Pre-tensioned; Diameter: 16; Grade: 12.9 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 22 - - Bolt hole type is standard - - Surface treatment: Sand blast (slip factor = 0.48) - - The ultimate strength of the weld material is, f\ :sub:`u` = 450 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are exposed to marine environment - - **Download:** - - DesignReport_1.2.1.2.1.1.2.pdf_. - - Example_1.2.1.2.1.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.2.1.2.1.2. Extended One way - Irreversible Moment - - **Sample Problem 1** - - Design a *Beam-Beam End Plate Splice* connection for a beam *UB 610 x 229 x 125*. The grade of the beam material is **E 300 (Fe 440)**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 300 kNm (irreversible) - - - **Shear Force**: 140 kN - - - **Axial Force**: 50 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24, 30; Grade: 8.8, 10.9 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 18, 20, 22 - - Bolt hole type is standard - - The ultimate strength of the weld material is, f\ :sub:`u` = 410 MPa - - The plate edges are hand flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.1.2.1.2.1.pdf_. - - Example_1.2.1.2.1.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Beam End Plate Splice* connection for a beam *LB 400*. The grade of the beam material is **E 250 (Fe 410 W)A**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 110 kNm (irreversible) - - - **Shear Force**: 55 kN - - - **Axial Force**: 15 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20; Grade: 5.8 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 12 - - Bolt hole type is standard - - The ultimate strength of the weld material is, f\ :sub:`u` = 410 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.1.2.1.2.2.pdf_. - - Example_1.2.1.2.1.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.2.1.2.1.3. Extended Both Ways - Reversible Moment - - **Sample Problem 1** - - Design a *Beam-Beam End Plate Splice* connection for a beam *WPB 360 X 300 X 91.4*. The grade of the beam material is **E 250 (Fe 410 W)A**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 120 kNm (reversible) - - - **Shear Force**: 75 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24; Grade: 4.6, 4.8, 5.6, 5.8 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16 - - Bolt hole type is standard - - The ultimate strength of the weld material is, f\ :sub:`u` = 410 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.1.2.1.3.1.pdf_. - - Example_1.2.1.2.1.3.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Beam End Plate Splice* connection for a beam *UB 406 x 140 x 46*. The grade of the beam material is **E 300 (Fe 440)**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 85 kNm (reversible) - - - **Shear Force**: 40 kN - - - **Axial Force**: 12 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Pre-tensioned; Diameter: 16; Grade: 12.9 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14 - - Bolt hole type is standard and bot type is Pre-tensioned - - Surface treatment: Sand blast (slip factor = 0.48) - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.1.2.1.3.2.pdf_. - - Example_1.2.1.2.1.3.2.osi_. - -.. note:: Example for **Release 2018-06-21** and lower versions - End Plate Splice Connection (Extended both ways) - - **Sample Problem 1** - - Design a bolted extended (both way) end plate spliced connection for a beam NPB 350x170x50.2, for transferring a factored reversible moment and shear force of 100 kNm and 40 kN. Use M20 bearing bolts of grade 9.8. Try an end plate 20 mm thick and weld of sizes 8 mm and 6 mm at flange and web, respectively. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). The bearing bolt is non-pretensioned and the bolt hole type is oversized. The type of weld is shop weld and the edge type is sheared or hand flame cut. - - **Download:** - - Design_Report_1.2.1.2.1.1.pdf_. - - Design_Example_1.2.1.2.1.1.osi_. - - - **Sample Problem 2** - - Design a bolted extended (both way) end plate spliced connection for a beam MB 450, for transferring a factored reversible moment, shear force and axial force of 170 kNm, 100 kN and 40 kN, respectively. Use M20 friction grip bolts of grade 8.8. Try an end plate 12 mm thick. The size of weld at flange and web is 6 mm and 8 mm. Take Fe410 grade steel (fy = 250 MPa, fu = 410 MPa). Assume the bolts to be pre-tensioned, bolt hole type is standard and the slip factor is 0.3. The type of weld is shop weld and the edge type is sheared or hand flame cut. - - **Download:** - - Design_Report_1.2.1.2.1.2.pdf_. - - Design_Example_1.2.1.2.1.2.osi_. - -1.2.1.3. Cover Plate Welded --------------------------------- - - **Sample Problem 1** - - Design a *Beam-Beam Cover Plate Splice* connection for a beam *UB 686 x 254 x 140*. The grade of the beam material is **E 300 (Fe 440)**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 235 kNm (reversible) - - - **Shear Force**: 150 kN - - - **Axial Force**: 20 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Flange Splice Plate:** Material E 250 (Fe 410 W)A; Preference: Outside + Inside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - **Web Splice Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - Type of weld fabrication: Field weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 550 MPa - - Gap between the members is 5 mm - - Members are exposed to (mild) marine environment - - **Download:** - - DesignReport_1.2.1.3.1.pdf_. - - Example_1.2.1.3.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Beam Cover Plate Splice* connection for a beam *WB 400*. The grade of the beam material is **E 250 (Fe 410 W)A**. - - The beam is subjected to the following loading condition; - - - **Bending Moment**: 150 kNm (reversible) - - - **Shear Force**: 80 kN - - Perform a *design check* by adopting the following design specifications: - - - **Flange Splice Plate:** Material E 300 (Fe 440); Preference: Outside + Inside; Thickness (mm): 18 - - **Web Splice Plate:** Material E 300 (Fe 440); Thickness (mm): 12 - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 450 MPa - - Gap between the members is 8 mm - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.2.1.3.2.pdf_. - - Example_1.2.1.3.2.osi_. - -1.2.2. Beam-to-Column Connection -******************* -1.2.2.1. End Plate -------------------------------- - -1.2.2.1.1. Column Flange-Beam Web (CFBW) connectivity - -1.2.2.1.1.1. Flushed - Reversible Moment - - **Sample Problem 1** - - Design a *Beam-Column End Plate* connection for a beam *WB 500* connected to a column *PBP 400 X 140.2*. The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 220 kNm (reversible) - - - **Shear Force**: 95 kN - - - **Axial Force**: 32 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 24, 30; Grade: 10.9, 12.9 - - **End Plate:** Material E 300 (Fe 440); Thickness (mm): 20, 22, 25, 28, 32 - - Bolt hole type is standard - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 510 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.2.2.1.1.1.1.pdf_. - - Example_1.2.2.1.1.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Column End Plate* connection for a beam *MB 500* connected to a column *UC 305 x 305 x 118*. The grade of the beam and the column material are **E 250 (Fe 410 W)A** and **E 300 (Fe 440)** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 150 kNm (reversible) - - - **Shear Force**: 90 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Pre-tensioned; Diameter: 20; Grade: 12.9 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 28 - - Bolt hole type is standard - - Surface treatment: Sand blast (slip factor = 0.48) - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 410 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are exposed to marine environment - - **Download:** - - DesignReport_1.2.2.1.1.1.2.pdf_. - - Example_1.2.2.1.1.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.2.2.1.1.2. Extended One way - Irreversible Moment - - **Sample Problem 1** - - Design a *Beam-Column End Plate* connection for a beam *WB 450* connected to a column *HB 450*. The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 210 kNm (reversible) - - - **Shear Force**: 40 kN - - - **Axial Force**: 15 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24, 30; Grade: 6.8, 8.8, 9.8 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20, 22 - - Bolt hole type is standard - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 410 MPa - - The plate edges are hand flame cut - - Gap between the members is 0 mm - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.2.2.1.1.2.1.pdf_. - - Example_1.2.2.1.1.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Column End Plate* connection for a beam *LB 450* connected to a column *PBP 300 X 124.2*. The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 100 kNm (reversible) - - - **Shear Force**: 55 kN - - - **Axial Force**: 12 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 24; Grade: 10.9 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16 - - Bolt hole type is standard - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 410 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.2.1.1.2.2.pdf_. - - Example_1.2.2.1.1.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.2.2.1.1.3. Extended Both Ways - Reversible Moment - - **Sample Problem 1** - - Design a *Beam-Column End Plate* connection for a beam *MB 500* connected to a column *PBP 400 X 158.1*. The grade of the beam and the column material are **E 300 (Fe 440)** and **E 350 (Fe 490)** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 180 kNm (reversible) - - - **Shear Force**: 100 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24, 30; Grade: 6.8, 8.8, 9.8 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16, 20, 22 - - Bolt hole type is standard - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 590 MPa - - The plate edges are hand flame cut - - Gap between the members is 0 mm - - Members are exposed to marine environment - - **Download:** - - DesignReport_1.2.2.1.1.3.1.pdf_. - - Example_1.2.2.1.1.3.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Column End Plate* connection for a beam *LB(P) 300* connected to a column *HB 300*. The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 90 kNm (reversible) - - - **Shear Force**: 45 kN - - - **Axial Force**: 10 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Pre-tensioned; Diameter: 16; Grade: 8.8 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 18 - - Bolt hole type is standard and bot type is Pre-tensioned - - Surface treatment: Clean mill scale (slip factor = 0.33) - - Type of weld fabrication: Shop weld - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.2.1.1.3.2.pdf_. - - Example_1.2.2.1.1.3.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.2.2.1.2. Column Web-Beam Web (CWBW) connectivity - -1.2.2.1.2.1. Flushed - Reversible Moment - - **Sample Problem 1** - - Design a *Beam-Column End Plate* connection for a beam *WB 300* connected to a column *HB 450*. The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 75 kNm (reversible) - - - **Shear Force**: 50 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 16, 20, 24, 30; Grade: 8.8, 9.8, 10.9 - - **End Plate:** Material E 300 (Fe 440); Thickness (mm): 14, 16, 18, 20 - - Bolt hole type is standard - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 490 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.2.1.2.1.1.pdf_. - - Example_1.2.2.1.2.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Column End Plate* connection for a beam *MB 300* connected to a column *UC 305 x 305 x 118*. The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 45 kNm (reversible) - - - **Shear Force**: 70 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20; Grade: 9.8 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - Bolt hole type is standard - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 435 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are exposed to marine environment - - **Download:** - - DesignReport_1.2.2.1.2.1.2.pdf_. - - Example_1.2.2.1.2.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.2.2.1.2.2. Extended One way - Irreversible Moment - - **Sample Problem 1** - - Design a *Beam-Column End Plate* connection for a beam *NPB 300 x 200 x 59.57* connected to a column *PBP 400 X 122.4*. The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 73 kNm (irreversible) - - - **Shear Force**: 50 kN - - - **Axial Force**: 15 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24; Grade: 8.8, 9.8 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14, 16, 18 - - Bolt hole type is standard - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 440 MPa - - The plate edges are hand flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.2.1.2.2.1.pdf_. - - Example_1.2.2.1.2.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Column End Plate* connection for a beam *LB 400* connected to a column *PBP 400 X 122.4*. The grade of the beam and the column material is **E 300 (Fe 440)** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 110 kNm (irreversible) - - - **Shear Force**: 55 kN - - - **Axial Force**: 15 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20; Grade: 4.8 - - **End Plate:** E 300 (Fe 440); Thickness (mm): 16 - - Bolt hole type is standard - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 450 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.2.1.2.2.2.pdf_. - - Example_1.2.2.1.2.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.2.2.1.2.3. Extended Both Ways - Reversible Moment - - **Sample Problem 1** - - Design a *Beam-Column End Plate* connection for a beam *UB 305 x 165 x 40* connected to a column *UC 356 x 406 x 235*. The grade of the beam and the column material are **E 300 (Fe 440)** and **E 350 (Fe 490)** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 65 kNm (reversible) - - - **Shear Force**: 150 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Pre-tensioned; Diameter: 24; Grade: 8.8 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20 - - Bolt hole type is standard and slip factor is 0.3 - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 450 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.2.1.2.3.1.pdf_. - - Example_1.2.2.1.2.3.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Beam-Column End Plate* connection for a beam *MB 500* connected to a column *PBP 400 X 212.5*. The grade of the beam and the column material is **E 250 (Fe 410 W)A** respectively. - - The load transferred from the beam to the column is as follows; - - - **Bending Moment**: 85 kNm (reversible) - - - **Shear Force**: 120 kN - - - **Axial Force**: 18 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Pre-tensioned; Diameter: 20; Grade: 8.8 - - **End Plate:** Material E 300 (Fe 440); Thickness (mm): 20 - - Bolt hole type is standard and bot type is Pre-tensioned - - Surface treatment: Blasted with short or girt (slip factor = 0.5) - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 500 MPa - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - **Download:** - - DesignReport_1.2.2.1.2.3.2.pdf_. - - Example_1.2.2.1.2.3.2.osi_. - -1.2.3. Column-to-Column Splice Connection -******************* -1.2.3.1. Cover Plate Bolted -------------------------------- - - **Sample Problem 1** - - Design a *Column-Column Cover Plate* bolted connection for a column *HB 300*. The grade of the column material is **E 300 (Fe 440)**. - - The column is subjected to the following loading condition; - - - **Bending Moment**: 127 kNm (reversible) - - - **Axial Force**: 320 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 20, 24, 30; Grade: 10.9, 12.9 - - **Flange Splice Plate:** Material E 300 (Fe 440); Preference: Outside + Inside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - **Web Splice Plate:** Material E 300 (Fe 440); Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - Bolt hole type is over-sized - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.2.3.1.1.pdf_. - - Example_1.2.3.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Column-Column Cover Plate* bolted connection for a column *PBP 400 X 176.1*. The grade of the column material is **E 300 (Fe 440)**. - - The column is subjected to the following loading condition; - - - **Bending Moment**: 60 kNm (reversible) - - - **Shear Force**: 40 kN - - - **Axial Force**: 520 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Pre-tensioned; Diameter: 24; Grade: 8.8 - - **Flange Splice Plate:** Material E 300 (Fe 440); Preference: Outside; Thickness (mm): 20 - - **Web Splice Plate:** Material E 300 (Fe 440); Thickness (mm): 16 - - Bolt hole type is standard - - Surface treatment: Sand blasted (slip factor = 0.52) - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are exposed to marine environment - - **Download:** - - DesignReport_1.2.3.1.2.pdf_. - - Example_1.2.3.1.2.osi_. - -1.2.3.2. Cover Plate Welded -------------------------------- - - **Sample Problem 1** - - Design a *Column-Column Cover Plate* welded connection for a column *PBP 400 X 140.2*. The grade of the column material is **E 300 (Fe 440)**. - - The column is subjected to the following loading condition; - - - **Bending Moment**: 127 kNm (reversible) - - - **Axial Force**: 370 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Flange Splice Plate:** Material E 300 (Fe 440); Preference: Outside + Inside; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - **Web Splice Plate:** Material E 300 (Fe 440); Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - Type of weld fabrication: Field weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 540 MPa - - Gap between the members is 3 mm - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.2.3.2.1.pdf_. - - Example_1.2.3.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Column-Column Cover Plate* welded connection for a column *HB 400*. The grade of the column material is **E 250 (Fe 410 W)A**. - - The column is subjected to the following loading condition; - - - **Bending Moment**: 50 kNm (reversible) - - - **Shear Force**: 30 kN - - - **Axial Force**: 480 kN - - Perform a *design check* by adopting the following design specifications: - - - **Flange Splice Plate:** Material E 250 (Fe 410 W)A; Preference: Outside; Thickness (mm): 18 - - **Web Splice Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14 - - Type of weld fabrication: Field weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 510 MPa - - Gap between the members is 5 mm - - Members are exposed to (mild) marine environment - - **Download:** - - DesignReport_1.2.3.2.2.pdf_. - - Example_1.2.3.2.2.osi_. - -1.2.3.3. End Plate -------------------------------- - -1.2.3.3.1. Flush End Plate - - **Sample Problem 1** - - Design a *Column-Column End Plate* connection for a column *HB 250*. The grade of the column material is **E 250 (Fe 410 W)A**. - - The column is subjected to the following loading condition; - - - **Axial Force**: 350 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 24, 30; Grade: 8.8, 9.8, 10.9, 12.9 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - Bolt hole type is standard - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.2.3.3.1.1.pdf_. - - Example_1.2.3.3.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Column-Column End Plate* connection for a column *PBP 300 X 95*. The grade of the column material is **E 300 (Fe 440)**. - - The column is subjected to the following loading condition; - - - **Bending Moment**: 50 kNm (reversible) - - - **Shear Force**: 20 kN - - - **Axial Force**: 250 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 30; Grade: 9.8 - - **End Plate:** Material E 300 (Fe 440); Thickness (mm): 32 - - Bolt hole type is standard - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.2.3.3.1.2.pdf_. - - Example_1.2.3.3.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.2.3.3.2. Extended Both Ways - - **Sample Problem 1** - - Design a *Column-Column End Plate* connection for a column *HB 250*. The grade of the column material is **E 250 (Fe 410 W)A**. - - The column is subjected to the following loading condition; - - - **Axial Force**: 350 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Bolt type:** Bearing bolt; Diameter: 24, 30; Grade: 8.8, 9.8, 10.9, 12.9 - - **End Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): Most optimum from the available thicknesses in the Osdag database - - Bolt hole type is standard - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.2.3.3.2.1.pdf_. - - Example_1.2.3.3.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Column-Column End Plate* connection for a column *PBP 300 X 95*. The grade of the column material is **E 300 (Fe 440)**. - - The column is subjected to the following loading condition; - - - **Bending Moment**: 50 kNm (reversible) - - - **Shear Force**: 20 kN - - - **Axial Force**: 250 kN - - Perform a *design check* by adopting the following design specifications: - - - **Bolt type:** Friction grip (HSFG); Pre-tensioned; Diameter: 30; Grade: 9.8 - - **End Plate:** Material E 300 (Fe 440); Thickness (mm): 32 - - Bolt hole type is standard - - The plate edges are machine-flame cut - - Gap between the members is 0 mm - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.2.3.3.2.2.pdf_. - - Example_1.2.3.3.2.2.osi_. - -1.3. *Base Plate Connection* -####################### - -1.3.1. Welded Column Base - - **Sample Problem 1** - - Design a *Base Plate* connection for a column *HB 450* subjected to the following reactions at the base; - - - **Axial Compression**: 1100 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Column:** Grade of the column material is **E 250 (Fe 410 W)A**. - - **Anchor Bolt:** Type: End Plate Type; Diameter: M20, M24, M30; Grade: 8.8, 9.8 - - **Footing:** Grade of concrete is M 25 - - **Base Plate:** Material E 250 (Fe 410 W)A - - Bolt hole type is over-sized and the Anchor bolt is *Galvanized* - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 510 MPa - - The plate edges are machine-flame cut - - Members are exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.3.1.1.pdf_. - - Example_1.3.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Base Plate* connection for a column *PBP 360 X 178.4* subjected to the following reactions at the base; - - - **Axial Compression**: 880 kN - - **Shear Force (at base)**: - - Along major (z-z) axis: 25 kN - - Along minor (y-y) axis: 10 kN - - Perform a *design check* by adopting the following design specifications: - - - **Column:** Grade of the column material is **E 300 (Fe 440)**. - - **Anchor Bolt:** Type: End Plate Type; Diameter: M20; Grade: 10.9 - - **Footing:** Grade of concrete is M 30 - - **Base Plate:** Material E 250 (Fe 410 W)A - - **Stiffener/Shear Key:** Material E 250 (Fe 410 W)A - - Bolt hole type is over-sized and the Anchor bolt is *Galvanized* - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 440 MPa - - The plate edges are machine-flame cut - - Members are exposed to corrosive environment - - **Download:** - - DesignReport_1.3.1.2.pdf_. - - Example_1.3.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.3.2. Moment Base Plate - - **Sample Problem 1** - - Design a *Base Plate* connection for a column *PBP 400 X 140.2* subjected to the following reactions at the base; - - - **Axial Compression**: 900 kN - - **Axial Tension/Uplift**: 27 kN - - **Shear Force (at base)**: - - Along major (z-z) axis: 85 kN - - Along minor (y-y) axis: 12 kN - - **Bending Moment**: - - Major (z-z) axis: 123 kN - - Perform an *optimum design* by adopting the following design specifications: - - - **Column:** Grade of the column material is **E 350 (Fe 490)**. - - **Anchor Bolt - Outside Column Flange:** Type: End Plate Type; Diameter: M24, M30; Grade: 8.8, 12.9 - - **Anchor Bolt - Inside Column Flange:** Type: End Plate Type; Diameter: M20, M24; Grade: 8.8, 9.8 - - **Footing:** Grade of concrete is M 30 - - **Base Plate:** Material E 250 (Fe 410 W)A - - **Stiffener/Shear Key:** Material E 250 (Fe 410 W)A - - Bolt hole type is over-sized and the Anchor bolt is *Galvanized* (both, outside and inside flange) - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 510 MPa - - The plate edges are machine-flame cut - - Members are exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.3.2.1.pdf_. - - Example_1.3.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Base Plate* connection for a column *HB 450* subjected to the following reactions at the base; - - - **Axial Compression**: 1600 kN - - **Shear Force (at base)**: - - Along major (z-z) axis: 80 kN - - Along minor (y-y) axis: 23 kN - - **Bending Moment**: - - Major (z-z) axis: 180 kN - - Minor (y-y) axis: 45 kN - - Perform a *design check* by adopting the following design specifications: - - - **Column:** Grade of the column material is **E 300 (Fe 440)**. - - **Anchor Bolt - Outside Column Flange:** Type: End Plate Type; Diameter: M30; Grade: 10.9 - - **Anchor Bolt - Inside Column Flange:** Type: End Plate Type; Diameter: M20; Grade: 8.8 - - **Footing:** Grade of concrete is M 25 - - **Base Plate:** Material E 300 (Fe 440) - - **Stiffener/Shear Key:** Material E 300 (Fe 440) - - Bolt hole type is over-sized and the Anchor bolt is *Galvanized* (both, outside and inside flange) - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 600 MPa - - The plate edges are machine-flame cut - - Members are not exposed to marine/corrosive environment - - **Download:** - - DesignReport_1.3.2.2.pdf_. - - Example_1.3.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -1.3.3. Hollow/Tubular Column Base - - **Sample Problem 1** - - Design a *Base Plate* connection for a square hollow column section *SHS 180 x 180 x 8* subjected to the following reactions at the base; - - - **Axial Compression**: 650 kN - - Perform an *optimum design* by adopting the following design specifications: - - - ** Hollow Column:** Grade of the column material is **E 300 (Fe 440)** - - **Anchor Bolt:** Type: End Plate Type; Diameter: M20, M24, M30; Grade: 8.8, 10.9 - - **Footing:** Grade of concrete is M 25 - - **Base Plate:** Material E 250 (Fe 410 W)A - - **Stiffener Plate:** Material E 250 (Fe 410 W)A - - Bolt hole type is over-sized and the Anchor bolt is *Galvanized* - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 510 MPa - - The plate edges are machine-flame cut - - Members are not exposed to corrosive environment - - **Download:** - - DesignReport_1.3.3.1.pdf_. - - Example_1.3.3.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Base Plate* connection for a circular hollow column section *CHS 355.6 x 10* subjected to the following reactions at the base; - - - **Axial Compression**: 775 kN - - **Shear Force (at base)**: - - Along major (z-z) axis: 45 kN - - Along minor (y-y) axis: 10 kN - - Perform a *design check* by adopting the following design specifications: - - - **Column:** Grade of the column material is **E 300 (Fe 440)**. - - **Anchor Bolt:** Type: End Plate Type; Diameter: M20; Grade: 8.8 - - **Footing:** Grade of concrete is M 35 - - **Base Plate:** Material E 250 (Fe 410 W)A - - **Stiffener/Shear Key:** Material E 250 (Fe 410 W)A - - Bolt hole type is over-sized and the Anchor bolt is *Galvanized* - - Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 440 MPa - - The plate edges are machine-flame cut - - Members are exposed to corrosive environment - - **Download:** - - DesignReport_1.3.3.2.pdf_. - - Example_1.3.3.2.osi_. - -*********************** -2. **Tension Member Design** -*********************** -2.1. *Tension Member - Bolted to End Gusset* -####################### - - **Sample Problem 1** - - Design a *Tension Member* with a bolted end connection to transfer a factored axial force of **235 kN**. - - Perform an *optimum design* by adopting the following design specifications: - - - **Section:** - - Profile: Angle - - Material Grade: E 250 (Fe 410 W)A - - Connection Location: Longer leg - - Length: 2560 mm - - - **End Connection:** - - Connector: Type: Bolted; Bolt type: Bearing bolt; Diameter: 16, 20; Grade: 4.6, 4.8 - - Bolt hole type is standard - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14, 16 - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_2.1.1.pdf_. - - Example_2.1.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Tension Member* with a bolted end connection to transfer a factored axial force of **330 kN**. - - Perform an *optimum design* by adopting the following design specifications: - - - **Section:** - - Profile: Back to Back Angles - - Material Grade: E 250 (Fe 410 W)A - - Connection Location: Longer leg - - Length: 2200 mm - - - **End Connection:** - - Connector: Type: Bolted; Bolt type: Bearing bolt; Diameter: 16, 20, 24; Grade: 6.8, 8.8 - - Bolt hole type is over-sized - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14, 16, 20 - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_2.1.2.pdf_. - - Example_2.1.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 3** - - Design a *Tension Member* with a bolted end connection to transfer a factored axial force of **180 kN**. - - Perform an *optimum design* by adopting the following design specifications: - - - **Section:** - - Profile: Star Angles - - Material Grade: E 165 (Fe 290) - - Connection Location: Short leg - - Length: 2500 mm - - - **End Connection:** - - Connector: Type: Bolted; Bolt type: Bearing bolt; Diameter: 16, 20; Grade: 5.6, 5.8, 6.8 - - Bolt hole type is standard - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14 - - The plate edges are hand flame cut - - **Download:** - - DesignReport_2.1.3.pdf_. - - Example_2.1.3.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 4** - - Design a *Tension Member* with a bolted end connection to transfer a factored axial force of **240 kN**. - - Perform a *design check* by adopting the following design specifications: - - - **Section:** - - Profile: Channel - - Designation: MC 250 - - Material Grade: E 250 (Fe 410 W)A - - Connection Location: Web - - Length: 3200 mm - - - **End Connection:** - - Connector: Type: Bolted; Bolt type: Friction grip (HSFG); Diameter: 20; Grade: 8.8 - - Bolt hole type is standard and slip factor is 0.3 - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14 - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_2.1.4.pdf_. - - Example_2.1.4.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 5** - - Design a *Tension Member* with a bolted end connection to transfer a factored axial force of **500 kN**. - - Perform an *optimum design* by adopting the following design specifications: - - - **Section:** - - Profile: Back to Back Channels - - Material Grade: E 300 (Fe 440) - - Connection Location: Web - - length: 3000 mm - - - **End Connection:** - - Connector: Type: Bolted; Bolt type: Bearing bolt; Diameter: 20, 24; Grade: 8.8, 9.8, 10.9 - - Bolt hole type is over-sized - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20 - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_2.1.5.pdf_. - - Example_2.1.5.osi_. - -2.2. *Tension Member - Welded to End Gusset* -####################### - - **Sample Problem 1** - - Design a *Tension Member* with a welded end connection to transfer a factored axial force of **235 kN**. - - Perform an *optimum design* by adopting the following design specifications: - - - **Section:** - - Profile: Angle - - Material Grade: E 250 (Fe 410 W)A - - Connection Location: Longer leg - - Length: 2560 mm - - - **End Connection:** - - Connector: Type: Welded; Type of weld fabrication: Field weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 450 MPa - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14, 16 - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_2.2.1.pdf_. - - Example_2.2.1.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 2** - - Design a *Tension Member* with bolted end connection to transfer a factored axial force of **330 kN**. - - Perform an *optimum design* by adopting the following design specifications: - - - **Section:** - - Profile: Back to Back Angles - - Material Grade: E 250 (Fe 410 W)A - - Connection Location: Longer leg - - Length: 2200 mm - - - **End Connection:** - - Connector: Type: Welded; Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 410 MPa - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14, 16, 20 - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_2.2.2.pdf_. - - Example_2.2.2.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 3** - - Design a *Tension Member* with bolted end connection to transfer a factored axial force of **180 kN**. - - Perform an *optimum design* by adopting the following design specifications: - - - **Section:** - - Profile: Star Angles - - Material Grade: E 165 (Fe 290) - - Connection Location: Short leg - - Length: 2500 mm - - - **End Connection:** - - Connector: Type: Welded; Type of weld fabrication: Field weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 410 MPa - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 10, 12, 14 - - The plate edges are hand flame cut - - **Download:** - - DesignReport_2.2.3.pdf_. - - Example_2.2.3.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 4** - - Design a *Tension Member* with bolted end connection to transfer a factored axial force of **240 kN**. - - Perform a *design check* by adopting the following design specifications: - - - **Section:** - - Profile: Channel - - Designation: MC 250 - - Material Grade: E 250 (Fe 410 W)A - - Connection Location: Web - - Length: 3200 mm - - - **End Connection:** - - Connector: Type: Welded; Type of weld fabrication: Field weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 410 MPa - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 14 - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_2.2.4.pdf_. - - Example_2.2.4.osi_. - --------------------------------------------------------------------------------------------------------------------------- - - **Sample Problem 5** - - Design a *Tension Member* with bolted end connection to transfer a factored axial force of **500 kN**. - - Perform an *optimum design* by adopting the following design specifications: - - - **Section:** - - Profile: Back to Back Channels - - Material Grade: E 300 (Fe 440) - - Connection Location: Web - - length: 3000 mm - - - **End Connection:** - - Connector: Type: Welded; Type of weld fabrication: Shop weld - - The ultimate strength of the weld material is, f\ :sub:`u` = 440 MPa - - **Gusset Plate:** Material E 250 (Fe 410 W)A; Thickness (mm): 16, 18, 20 - - The plate edges are machine-flame cut - - **Download:** - - DesignReport_2.2.5.pdf_. - - Example_2.2.5.osi_. - --------------------------------------------------------------------------------------------------------------------------- - -**Other Resources** - - - How to use Osdag? Learn through Osdag's audio-video Tutorials_ - - - We are on YouTube_ as well. Like and Subscribe our channel to keep up-to-date with Osdag - - - Comments, questions, suggestions? Check the forum Discussion_ - - - Or, ask us a query directly through the FOSSEE Forum_ - --------------------------------------------------------------------------------------------------------------------------- - - Copyright © 2017 Osdag. All rights reserved. - -.. _Homepage: http://osdag.fossee.in - -.. _Tutorials: https://osdag.fossee.in/resources/videos - -.. _YouTube: https://www.youtube.com/channel/UCnSZ7EjhDwNi3eCPcSKpgJg - -.. _Discussion: https://osdag.fossee.in/forum - -.. _Forum: https://forums.fossee.in/ - -.. _DesignReport_1.1.1.1.1.pdf: \DesignReport_1.1.1.1.1.pdf -.. _Example_1.1.1.1.1.osi: \Example_1.1.1.1.1.osi - -.. _DesignReport_1.1.1.1.2.pdf: \DesignReport_1.1.1.1.2.pdf -.. _Example_1.1.1.1.2.osi: \Example_1.1.1.1.2.osi - -.. _DesignReport_1.1.1.2.1.pdf: \DesignReport_1.1.1.2.1.pdf -.. _Example_1.1.1.2.1.osi: \Example_1.1.1.2.1.osi - -.. _DesignReport_1.1.1.2.2.pdf: \DesignReport_1.1.1.2.2.pdf -.. _Example_1.1.1.2.2.osi: \Example_1.1.1.2.2.osi - -.. _DesignReport_1.1.1.3.1.pdf: \DesignReport_1.1.1.3.1.pdf -.. _Example_1.1.1.3.1.osi: \Example_1.1.1.3.1.osi - -.. _DesignReport_1.1.1.3.2.pdf: \DesignReport_1.1.1.3.2.pdf -.. _Example_1.1.1.3.2.osi: \Example_1.1.1.3.2.osi - -.. _DesignReport_1.1.2.1.1.pdf: \DesignReport_1.1.2.1.1.pdf -.. _Example_1.1.2.1.1.osi: \Example_1.1.2.1.1.osi - -.. _DesignReport_1.1.2.1.2.pdf: \DesignReport_1.1.2.1.2.pdf -.. _Example_1.1.2.1.2.osi: \Example_1.1.2.1.2.osi - -.. _DesignReport_1.1.2.2.1.pdf: \DesignReport_1.1.2.2.1.pdf -.. _Example_1.1.2.2.1.osi: \Example_1.1.2.2.1.osi - -.. _DesignReport_1.1.2.2.2.pdf: \DesignReport_1.1.2.2.2.pdf -.. _Example_1.1.2.2.2.osi: \Example_1.1.2.2.2.osi - -.. _DesignReport_1.1.2.3.1.pdf: \DesignReport_1.1.2.3.1.pdf -.. _Example_1.1.2.3.1.osi: \Example_1.1.2.3.1.osi - -.. _DesignReport_1.1.2.3.2.pdf: \DesignReport_1.1.2.3.2.pdf -.. _Example_1.1.2.3.2.osi: \Example_1.1.2.3.2.osi - -.. _DesignReport_1.1.3.1.1.pdf: \DesignReport_1.1.3.1.1.pdf -.. _Example_1.1.3.1.1.osi: \Example_1.1.3.1.1.osi - -.. _DesignReport_1.1.3.1.2.pdf: \DesignReport_1.1.3.1.2.pdf -.. _Example_1.1.3.1.2.osi: \Example_1.1.3.1.2.osi - -.. _DesignReport_1.1.3.2.1.pdf: \DesignReport_1.1.3.2.1.pdf -.. _Example_1.1.3.2.1.osi: \Example_1.1.3.2.1.osi - -.. _DesignReport_1.1.3.2.2.pdf: \DesignReport_1.1.3.2.2.pdf -.. _Example_1.1.3.2.2.osi: \Example_1.1.3.2.2.osi - -.. _DesignReport_1.1.3.3.1.pdf: \DesignReport_1.1.3.3.1.pdf -.. _Example_1.1.3.3.1.osi: \Example_1.1.3.3.1.osi - -.. _DesignReport_1.1.3.3.2.pdf: \DesignReport_1.1.3.3.2.pdf -.. _Example_1.1.3.3.2.osi: \Example_1.1.3.3.2.osi - -.. _DesignReport_1.1.4.1.1.pdf: \DesignReport_1.1.4.1.1.pdf -.. _Example_1.1.4.1.1.osi: \Example_1.1.4.1.1.osi - -.. _DesignReport_1.1.4.1.2.pdf: \DesignReport_1.1.4.1.2.pdf -.. _Example_1.1.4.1.2.osi: \Example_1.1.4.1.2.osi - -.. _DesignReport_1.1.4.2.1.pdf: \DesignReport_1.1.4.2.1.pdf -.. _Example_1.1.4.2.1.osi: \Example_1.1.4.2.1.osi - -.. _DesignReport_1.1.4.2.2.pdf: \DesignReport_1.1.4.2.2.pdf -.. _Example_1.1.4.2.2.osi: \Example_1.1.4.2.2.osi - -.. _DesignReport_1.2.1.1.1.pdf: \DesignReport_1.2.1.1.1.pdf -.. _Example_1.2.1.1.1.osi: \Example_1.2.1.1.1.osi - -.. _DesignReport_1.2.1.1.2.pdf: \DesignReport_1.2.1.1.2.pdf -.. _Example_1.2.1.1.2.osi: \Example_1.2.1.1.2.osi - - -.. _DesignReport_1.2.1.2.1.1.1.pdf: \DesignReport_1.2.1.2.1.1.1.pdf -.. _Example_1.2.1.2.1.1.1.osi: \Example_1.2.1.2.1.1.1.osi - - -.. _DesignReport_1.2.1.2.1.1.2.pdf: \DesignReport_1.2.1.2.1.1.2.pdf -.. _Example_1.2.1.2.1.1.2.osi: \Example_1.2.1.2.1.1.2.osi - -.. _DesignReport_1.2.1.2.1.2.1.pdf: \DesignReport_1.2.1.2.1.2.1.pdf -.. _Example_1.2.1.2.1.2.1.osi: \Example_1.2.1.2.1.2.1.osi - - -.. _DesignReport_1.2.1.2.1.2.2.pdf: \DesignReport_1.2.1.2.1.2.2.pdf -.. _Example_1.2.1.2.1.2.2.osi: \Example_1.2.1.2.1.2.2.osi - - -.. _DesignReport_1.2.1.2.1.3.1.pdf: \DesignReport_1.2.1.2.1.3.1.pdf -.. _Example_1.2.1.2.1.3.1.osi: \Example_1.2.1.2.1.3.1.osi - - -.. _DesignReport_1.2.1.2.1.3.2.pdf: \DesignReport_1.2.1.2.1.3.2.pdf -.. _Example_1.2.1.2.1.3.2.osi: \Example_1.2.1.2.1.3.2.osi - -.. _Design_Report_1.2.1.2.1.1.pdf: \Design_Report_1.2.1.2.1.1.pdf -.. _Design_Example_1.2.1.2.1.1.osi: \Design_Example_1.2.1.2.1.1.osi - -.. _Design_Report_1.2.1.2.1.2.pdf: \Design_Report_1.2.1.2.1.2.pdf -.. _Design_Example_1.2.1.2.1.2.osi: \Design_Example_1.2.1.2.1.2.osi - -.. _DesignReport_1.2.1.3.1.pdf: \DesignReport_1.2.1.3.1.pdf -.. _Example_1.2.1.3.1.osi: \Example_1.2.1.3.1.osi - - -.. _DesignReport_1.2.1.3.2.pdf: \DesignReport_1.2.1.3.2.pdf -.. _Example_1.2.1.3.2.osi: \Example_1.2.1.3.2.osi - - -.. _DesignReport_1.2.2.1.1.1.1.pdf: \DesignReport_1.2.2.1.1.1.1.pdf -.. _Example_1.2.2.1.1.1.1.osi: \Example_1.2.2.1.1.1.1.osi - - -.. _DesignReport_1.2.2.1.1.1.2.pdf: \DesignReport_1.2.2.1.1.1.2.pdf - -.. _Example_1.2.2.1.1.1.2.osi: \Example_1.2.2.1.1.1.2.osi - - -.. _DesignReport_1.2.2.1.1.2.1.pdf: \DesignReport_1.2.2.1.1.2.1.pdf - -.. _Example_1.2.2.1.1.2.1.osi: \Example_1.2.2.1.1.2.1.osi - - -.. _DesignReport_1.2.2.1.1.2.2.pdf: \DesignReport_1.2.2.1.1.2.2.pdf - -.. _Example_1.2.2.1.1.2.2.osi: \Example_1.2.2.1.1.2.2.osi - - -.. _DesignReport_1.2.2.1.1.3.1.pdf: \DesignReport_1.2.2.1.1.3.1.pdf - -.. _Example_1.2.2.1.1.3.1.osi: \Example_1.2.2.1.1.3.1.osi - - -.. _DesignReport_1.2.2.1.1.3.2.pdf: \DesignReport_1.2.2.1.1.3.2.pdf - -.. _Example_1.2.2.1.1.3.2.osi: \Example_1.2.2.1.1.3.2.osi - - -.. _DesignReport_1.2.2.1.2.1.1.pdf: \DesignReport_1.2.2.1.2.1.1.pdf - -.. _Example_1.2.2.1.2.1.1.osi: \Example_1.2.2.1.2.1.1.osi - - -.. _DesignReport_1.2.2.1.2.1.2.pdf: \DesignReport_1.2.2.1.2.1.2.pdf - -.. _Example_1.2.2.1.2.1.2.osi: \Example_1.2.2.1.2.1.2.osi - - -.. _DesignReport_1.2.2.1.2.2.1.pdf: \DesignReport_1.2.2.1.2.2.1.pdf - -.. _Example_1.2.2.1.2.2.1.osi: \Example_1.2.2.1.2.2.1.osi - - -.. _DesignReport_1.2.2.1.2.2.2.pdf: \DesignReport_1.2.2.1.2.2.2.pdf - -.. _Example_1.2.2.1.2.2.2.osi: \Example_1.2.2.1.2.2.2.osi - -.. _DesignReport_1.2.2.1.2.3.1.pdf: \DesignReport_1.2.2.1.2.3.1.pdf - -.. _Example_1.2.2.1.2.3.1.osi: \Example_1.2.2.1.2.3.1.osi - -.. _DesignReport_1.2.2.1.2.3.2.pdf: \DesignReport_1.2.2.1.2.3.2.pdf - -.. _Example_1.2.2.1.2.3.2.osi: \Example_1.2.2.1.2.3.2.osi - -.. _DesignReport_1.2.3.1.1.pdf: \DesignReport_1.2.3.1.1.pdf - -.. _Example_1.2.3.1.1.osi: \Example_1.2.3.1.1.osi - -.. _DesignReport_1.2.3.1.2.pdf: \DesignReport_1.2.3.1.2.pdf - -.. _Example_1.2.3.1.2.osi: \Example_1.2.3.1.2.osi - -.. _DesignReport_1.2.3.2.1.pdf: \DesignReport_1.2.3.2.1.pdf - -.. _Example_1.2.3.2.1.osi: \Example_1.2.3.2.1.osi - -.. _DesignReport_1.2.3.2.2.pdf: \DesignReport_1.2.3.2.2.pdf - -.. _Example_1.2.3.2.2.osi: \Example_1.2.3.2.2.osi - -.. _DesignReport_1.2.3.3.1.1.pdf: \DesignReport_1.2.3.3.1.1.pdf - -.. _Example_1.2.3.3.1.1.osi: \Example_1.2.3.3.1.1.osi - -.. _DesignReport_1.2.3.3.1.2.pdf: \DesignReport_1.2.3.3.1.2.pdf - -.. _Example_1.2.3.3.1.2.osi: \Example_1.2.3.3.1.2.osi - -.. _DesignReport_1.2.3.3.2.1.pdf: \DesignReport_1.2.3.3.2.1.pdf - -.. _Example_1.2.3.3.2.1.osi: \Example_1.2.3.3.2.1.osi - -.. _DesignReport_1.2.3.3.2.2.pdf: \DesignReport_1.2.3.3.2.2.pdf - -.. _Example_1.2.3.3.2.2.osi: \Example_1.2.3.3.2.2.osi - -.. _DesignReport_1.3.1.1.pdf: \DesignReport_1.3.1.1.pdf - -.. _Example_1.3.1.1.osi: \Example_1.3.1.1.osi - -.. _DesignReport_1.3.1.2.pdf: \DesignReport_1.3.1.2.pdf - -.. _Example_1.3.1.2.osi: \Example_1.3.1.2.osi - -.. _DesignReport_1.3.2.1.pdf: \DesignReport_1.3.2.1.pdf - -.. _Example_1.3.2.1.osi: \Example_1.3.2.1.osi - -.. _DesignReport_1.3.2.2.pdf: \DesignReport_1.3.2.2.pdf - -.. _Example_1.3.2.2.osi: \Example_1.3.2.2.osi - -.. _DesignReport_1.3.3.1.pdf: \DesignReport_1.3.3.1.pdf - -.. _Example_1.3.3.1.osi: \Example_1.3.3.1.osi - -.. _DesignReport_1.3.3.2.pdf: \DesignReport_1.3.3.2.pdf - -.. _Example_1.3.3.2.osi: \Example_1.3.3.2.osi - -.. _DesignReport_2.1.1.pdf: \DesignReport_2.1.1.pdf - -.. _Example_2.1.1.osi: \Example_2.1.1.osi - -.. _DesignReport_2.1.2.pdf: \DesignReport_2.1.2.pdf - -.. _Example_2.1.2.osi: \Example_2.1.2.osi - -.. _DesignReport_2.1.3.pdf: \DesignReport_2.1.3.pdf - -.. _Example_2.1.3.osi: \Example_2.1.3.osi - -.. _DesignReport_2.1.4.pdf: \DesignReport_2.1.4.pdf - -.. _Example_2.1.4.osi: \Example_2.1.4.osi - -.. _DesignReport_2.1.5.pdf: \DesignReport_2.1.5.pdf - -.. _Example_2.1.5.osi: \Example_2.1.5.osi - -.. _DesignReport_2.2.1.pdf: \DesignReport_2.2.1.pdf - -.. _Example_2.2.1.osi: \Example_2.2.1.osi - -.. _DesignReport_2.2.2.pdf: \DesignReport_2.2.2.pdf - -.. _Example_2.2.2.osi: \Example_2.2.2.osi - -.. _DesignReport_2.2.3.pdf: \DesignReport_2.2.3.pdf - -.. _Example_2.2.3.osi: \Example_2.2.3.osi - -.. _DesignReport_2.2.4.pdf: \DesignReport_2.2.4.pdf - -.. _Example_2.2.4.osi: \Example_2.2.4.osi - -.. _DesignReport_2.2.5.pdf: \DesignReport_2.2.5.pdf - -.. _Example_2.2.5.osi: \Example_2.2.5.osi - -.. _Design_Report_1.2.1.1.1.2.pdf: \Design_Report_1.2.1.1.1.2.pdf - -.. _Design_Example_1.2.1.1.1.2.osi: \Design_Example_1.2.1.1.1.2.osi - -.. _Design_Report_1.2.1.1.1.1.pdf: \Design_Report_1.2.1.1.1.1.pdf - -.. _Design_Example_1.2.1.1.1.1.osi: \Design_Example_1.2.1.1.1.1.osi - -.. _Design_Report_1.1.4.1.1.pdf: \Design_Report_1.1.4.1.1.pdf - -.. _Design_Example_1.1.4.1.1.osi: \Design_Example_1.1.4.1.1.osi - -.. _Design_Report_1.1.4.1.2.pdf: \Design_Report_1.1.4.1.2.pdf - -.. _Design_Example_1.1.4.1.2.osi: \Design_Example_1.1.4.1.2.osi - -.. _Design_Report_1.1.4.2.1.pdf: \Design_Report_1.1.4.2.1.pdf - -.. _Design_Example_1.1.4.2.1.osi: \Design_Example_1.1.4.2.1.osi - -.. _Design_Report_1.1.4.2.2.pdf: \Design_Report_1.1.4.2.2.pdf - -.. _Design_Example_1.1.4.2.2.osi: \Design_Example_1.1.4.2.2.osi - -.. _Design_Report_1.1.3.1.1.pdf: \Design_Report_1.1.3.1.1.pdf - -.. _Design_Example_1.1.3.1.1.osi: \Design_Example_1.1.3.1.1.osi - -.. _Design_Report_1.1.3.1.2.pdf: \Design_Report_1.1.3.1.2.pdf - -.. _Design_Example_1.1.3.1.2.osi: \Design_Example_1.1.3.1.2.osi - -.. _Design_Report_1.1.3.2.1.pdf: \Design_Report_1.1.3.2.1.pdf - -.. _Design_Example_1.1.3.2.1.osi: \Design_Example_1.1.3.2.1.osi - -.. _Design_Report_1.1.3.2.2.pdf: \Design_Report_1.1.3.2.2.pdf - -.. _Design_Example_1.1.3.2.2.osi: \Design_Example_1.1.3.2.2.osi - -.. _Design_Report_1.1.3.3.1.pdf: \Design_Report_1.1.3.3.1.pdf - -.. _Design_Example_1.1.3.3.1.osi: \Design_Example_1.1.3.3.1.osi - -.. _Design_Report_1.1.3.3.2.pdf: \Design_Report_1.1.3.3.2.pdf - -.. _Design_Example_1.1.3.3.2.osi: \Design_Example_1.1.3.3.2.osi - -.. _Design_Report_1.1.1.1.1.pdf: \Design_Report_1.1.1.1.1.pdf - -.. _Design_Example_1.1.1.1.1.osi: \Design_Example_1.1.1.1.1.osi - -.. _Design_Report_1.1.1.1.2.pdf: \Design_Report_1.1.1.1.2.pdf - -.. _Design_Example_1.1.1.1.2.osi: \Design_Example_1.1.1.1.2.osi - -.. _Design_Report_1.1.1.2.1.pdf: \Design_Report_1.1.1.2.1.pdf - -.. _Design_Example_1.1.1.2.1.osi: \Design_Example_1.1.1.2.1.osi - -.. _Design_Report_1.1.1.2.2.pdf: \Design_Report_1.1.1.2.2.pdf - -.. _Design_Example_1.1.1.2.2.osi: \Design_Example_1.1.1.2.2.osi - -.. _Design_Report_1.1.1.3.1.pdf: \Design_Report_1.1.1.3.1.pdf - -.. _Design_Example_1.1.1.3.1.osi: \Design_Example_1.1.1.3.1.osi - -.. _Design_Report_1.1.1.3.2.pdf: \Design_Report_1.1.1.3.2.pdf - -.. _Design_Example_1.1.1.3.2.osi: \Design_Example_1.1.1.3.2.osi - -.. _Design_Report_1.1.2.1.1.pdf: \Design_Report_1.1.2.1.1.pdf - -.. _Design_Example_1.1.2.1.1.osi: \Design_Example_1.1.2.1.1.osi - -.. _Design_Report_1.1.2.1.2.pdf: \Design_Report_1.1.2.1.2.pdf - -.. _Design_Example_1.1.2.1.2.osi: \Design_Example_1.1.2.1.2.osi - -.. _Design_Report_1.1.2.2.1.pdf: \Design_Report_1.1.2.2.1.pdf - -.. _Design_Example_1.1.2.2.1.osi: \Design_Example_1.1.2.2.1.osi - -.. _Design_Report_1.1.2.2.2.pdf: \Design_Report_1.1.2.2.2.pdf - -.. _Design_Example_1.1.2.2.2.osi: \Design_Example_1.1.2.2.2.osi - -.. _Design_Report_1.1.2.3.1.pdf: \Design_Report_1.1.2.3.1.pdf - -.. _Design_Example_1.1.2.3.1.osi: \Design_Example_1.1.2.3.1.osi - -.. _Design_Report_1.1.2.3.2.pdf: \Design_Report_1.1.2.3.2.pdf - -.. _Design_Example_1.1.2.3.2.osi: \Design_Example_1.1.2.3.2.osi - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - diff --git a/ResourceFiles/html_page/make.bat b/ResourceFiles/html_page/make.bat deleted file mode 100644 index 5be40d069..000000000 --- a/ResourceFiles/html_page/make.bat +++ /dev/null @@ -1,36 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build -set SPHINXPROJ=Osdag_design_examples - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/ResourceFiles/images/1.RRFF.PNG b/ResourceFiles/images/1.RRFF.PNG deleted file mode 100644 index 3127aa0da..000000000 Binary files a/ResourceFiles/images/1.RRFF.PNG and /dev/null differ diff --git a/ResourceFiles/images/1.RRFF_rotated.PNG b/ResourceFiles/images/1.RRFF_rotated.PNG deleted file mode 100644 index c64e240be..000000000 Binary files a/ResourceFiles/images/1.RRFF_rotated.PNG and /dev/null differ diff --git a/ResourceFiles/images/2.FRFR.PNG b/ResourceFiles/images/2.FRFR.PNG deleted file mode 100644 index 84c8be345..000000000 Binary files a/ResourceFiles/images/2.FRFR.PNG and /dev/null differ diff --git a/ResourceFiles/images/2.FRFR_rotated.PNG b/ResourceFiles/images/2.FRFR_rotated.PNG deleted file mode 100644 index 0af2feb35..000000000 Binary files a/ResourceFiles/images/2.FRFR_rotated.PNG and /dev/null differ diff --git a/ResourceFiles/images/3.RFRF.PNG b/ResourceFiles/images/3.RFRF.PNG deleted file mode 100644 index ec68f67dd..000000000 Binary files a/ResourceFiles/images/3.RFRF.PNG and /dev/null differ diff --git a/ResourceFiles/images/4.RRFR.PNG b/ResourceFiles/images/4.RRFR.PNG deleted file mode 100644 index 95a71bc6c..000000000 Binary files a/ResourceFiles/images/4.RRFR.PNG and /dev/null differ diff --git a/ResourceFiles/images/4.RRFR_rotated.PNG b/ResourceFiles/images/4.RRFR_rotated.PNG deleted file mode 100644 index 505d6afc5..000000000 Binary files a/ResourceFiles/images/4.RRFR_rotated.PNG and /dev/null differ diff --git a/ResourceFiles/images/5.RRRF.PNG b/ResourceFiles/images/5.RRRF.PNG deleted file mode 100644 index 2e5732b5e..000000000 Binary files a/ResourceFiles/images/5.RRRF.PNG and /dev/null differ diff --git a/ResourceFiles/images/5.RRRF_rotated.PNG b/ResourceFiles/images/5.RRRF_rotated.PNG deleted file mode 100644 index d8ea459f8..000000000 Binary files a/ResourceFiles/images/5.RRRF_rotated.PNG and /dev/null differ diff --git a/ResourceFiles/images/6.RRRR.PNG b/ResourceFiles/images/6.RRRR.PNG deleted file mode 100644 index f1eae1c1a..000000000 Binary files a/ResourceFiles/images/6.RRRR.PNG and /dev/null differ diff --git a/ResourceFiles/images/BC_CF-BW-Flush.png b/ResourceFiles/images/BC_CF-BW-Flush.png deleted file mode 100644 index 1481c310a..000000000 Binary files a/ResourceFiles/images/BC_CF-BW-Flush.png and /dev/null differ diff --git a/ResourceFiles/images/colF2.png b/ResourceFiles/images/colF2.png deleted file mode 100644 index 2a2175019..000000000 Binary files a/ResourceFiles/images/colF2.png and /dev/null differ diff --git a/ResourceFiles/images/colW1.png b/ResourceFiles/images/colW1.png deleted file mode 100644 index 0c4f9ca5b..000000000 Binary files a/ResourceFiles/images/colW1.png and /dev/null differ diff --git a/ResourceFiles/images/fin_beam_beam.png b/ResourceFiles/images/fin_beam_beam.png deleted file mode 100644 index d01bbd43e..000000000 Binary files a/ResourceFiles/images/fin_beam_beam.png and /dev/null differ diff --git a/ResourceFiles/images/fin_cf_bw.png b/ResourceFiles/images/fin_cf_bw.png deleted file mode 100644 index 2a2175019..000000000 Binary files a/ResourceFiles/images/fin_cf_bw.png and /dev/null differ diff --git a/ResourceFiles/images/fin_cw_bw.png b/ResourceFiles/images/fin_cw_bw.png deleted file mode 100644 index 6e04994a5..000000000 Binary files a/ResourceFiles/images/fin_cw_bw.png and /dev/null differ diff --git a/ResourceFiles/images/flush_ep.png b/ResourceFiles/images/flush_ep.png deleted file mode 100644 index 014b6bde3..000000000 Binary files a/ResourceFiles/images/flush_ep.png and /dev/null differ diff --git a/__init__.py b/__init__.py deleted file mode 100644 index b389e2626..000000000 --- a/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from _version import __version__ \ No newline at end of file diff --git a/_version.py b/_version.py deleted file mode 100644 index c0f2b9843..000000000 --- a/_version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "2021.02.a.a12f" diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 000000000..759b0eadf --- /dev/null +++ b/backend/__init__.py @@ -0,0 +1,2 @@ +# Backend package + diff --git a/backend/apps/__init__.py b/backend/apps/__init__.py new file mode 100644 index 000000000..8c0520a62 --- /dev/null +++ b/backend/apps/__init__.py @@ -0,0 +1,2 @@ +# Apps package + diff --git a/APP_CRASH/Appcrash/_dialogs/__init__.py b/backend/apps/core/__init__.py similarity index 100% rename from APP_CRASH/Appcrash/_dialogs/__init__.py rename to backend/apps/core/__init__.py diff --git a/backend/apps/core/admin.py b/backend/apps/core/admin.py new file mode 100644 index 000000000..67e3dddb9 --- /dev/null +++ b/backend/apps/core/admin.py @@ -0,0 +1,27 @@ +from django.contrib import admin + +# import models +from apps.core.models import Anchor_Bolt , Angle_Pitch , Angles , Beams , Bolt , Bolt_fy_fu , CHS , Channels , Columns , EqualAngle , Material, RHS , SHS , UnequalAngle +from apps.core.models import Design, UserAccount +######################################################### +# Author : Atharva Pingale ( FOSSEE Summer Fellow '23 ) # +######################################################### + + +# Register your models here. +admin.site.register(Anchor_Bolt) +admin.site.register(Angle_Pitch) +admin.site.register(Angles) +admin.site.register(Beams) +admin.site.register(Bolt) +admin.site.register(Bolt_fy_fu) +admin.site.register(CHS) +admin.site.register(Channels) +admin.site.register(Columns) +admin.site.register(EqualAngle) +admin.site.register(Material) +admin.site.register(RHS) +admin.site.register(SHS) +admin.site.register(UnequalAngle) +admin.site.register(Design) +admin.site.register(UserAccount) diff --git a/backend/apps/core/api/__init__.py b/backend/apps/core/api/__init__.py new file mode 100644 index 000000000..19fb33f21 --- /dev/null +++ b/backend/apps/core/api/__init__.py @@ -0,0 +1,43 @@ +""" +Core API package - Re-exports +""" +# Auth exports +from .auth.user_view import ( + ObtainInputFileView, SaveInputFileView +) +from .auth.jwt_api import JWTHomeView +from .auth.google_sso_api import GoogleSSOView + +# Project exports +from .projects.project_api import ProjectAPI, ProjectDetailAPI, ProjectByNameAPI +from .projects.osi_api import SaveOsiFromInputs, OpenOsiUpload, OpenOsiById, ModuleRoutes, ProjectOsiDownload + +# Design exports +from .design.design_pref_api import DesignPreference, MaterialDetails +from .design.design_report_csv_view import CompanyLogoView, CreateDesignReport, GetPDF +from .design.report_customization_api import ParseReportSections, CustomizeReport + +# CAD exports +from .cad.cad_model_api import CADGeneration +from .cad.cad_model_download import CADDownload + +# Modules exports +from .modules.modules_api import GetModules + +__all__ = [ + # Auth + 'ObtainInputFileView', 'SaveInputFileView', + 'JWTHomeView', 'GoogleSSOView', + # Projects + 'ProjectAPI', 'ProjectDetailAPI', 'ProjectByNameAPI', + 'SaveOsiFromInputs', 'OpenOsiUpload', 'OpenOsiById', 'ModuleRoutes', 'ProjectOsiDownload', + # Design + 'DesignPreference', 'MaterialDetails', 'CompanyLogoView', + 'CreateDesignReport', 'GetPDF', + 'ParseReportSections', 'CustomizeReport', + # CAD + 'CADGeneration', 'CADDownload', + # Modules + 'GetModules', +] + diff --git a/backend/apps/core/api/auth/__init__.py b/backend/apps/core/api/auth/__init__.py new file mode 100644 index 000000000..1e8003dfd --- /dev/null +++ b/backend/apps/core/api/auth/__init__.py @@ -0,0 +1,14 @@ +""" +Authentication & Authorization APIs +""" +from .user_view import ( + ObtainInputFileView, SaveInputFileView +) +from .jwt_api import JWTHomeView +from .google_sso_api import GoogleSSOView + +__all__ = [ + 'ObtainInputFileView', 'SaveInputFileView', + 'JWTHomeView', 'GoogleSSOView', +] + diff --git a/backend/apps/core/api/auth/google_sso_api.py b/backend/apps/core/api/auth/google_sso_api.py new file mode 100644 index 000000000..eb0e0554d --- /dev/null +++ b/backend/apps/core/api/auth/google_sso_api.py @@ -0,0 +1,14 @@ +# DRF imports +from rest_framework.response import Response +from rest_framework import status +from rest_framework.views import APIView + +# defining gthe APIviews here +class GoogleSSOView(APIView) : + + def get(self , request): + print('inside teh GoogleSSOView') + + print('APIview under development') + + return Response({'message' , 'GoogleSSO under development'} , status = status.HTTP_200_OK) diff --git a/backend/apps/core/api/auth/jwt_api.py b/backend/apps/core/api/auth/jwt_api.py new file mode 100644 index 000000000..d24639979 --- /dev/null +++ b/backend/apps/core/api/auth/jwt_api.py @@ -0,0 +1,20 @@ +######################################################### +# Author : Atharva Pinagle ( FOSSEE SUmmer Fellow '23 ) # +######################################################### + +# DRF imports +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated +from rest_framework import status + +class JWTHomeView(APIView) : + + permission_classes = (IsAuthenticated,) + # add this permission class to authenticate the user for all the views of the project + + def get(self, request) : + print('inside the JWTHomeView get') + + content = {'message' , 'Welcome to the JWT Authentication page'} + return Response(content , status = status.HTTP_200_OK) diff --git a/backend/apps/core/api/auth/user_view.py b/backend/apps/core/api/auth/user_view.py new file mode 100644 index 000000000..5cc02120e --- /dev/null +++ b/backend/apps/core/api/auth/user_view.py @@ -0,0 +1,128 @@ +######################################################### +# Author : Atharva Pingale ( FOSSEE Summer Fellow '23 ) # +######################################################### + +# DRF imports +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from rest_framework.parsers import JSONParser +from rest_framework.permissions import IsAuthenticated + +# importing Django models +from apps.core.models import UserAccount + +# importing exceptions +from django.core.exceptions import ObjectDoesNotExist + +# django imports +from django.conf import settings +from django.core.files.storage import default_storage +from django.http import FileResponse, JsonResponse + +# importing serializers +from apps.core.serializers import UserAccount_Serializer, OsiFileSerializer +from apps.core.models import OsiFile + +# other imports +from django.contrib.auth.models import User +import os +import uuid +import base64 + + +# obtain the attributes +SECRET_ROOT = getattr(settings, 'SECRET_ROOT' , "") + + +def convert_to_32_bytes(input_string) : + input_bytes = input_string.encode('utf-8') + padded_bytes = input_bytes.ljust(32, b'\x00') + + return padded_bytes + +# Removed: SignupView, LoginView, ForgetPasswordView, CheckEmailView +# All authentication now handled by Firebase via FirebaseAuthView in views.py + +# Removed: LogoutView - Firebase handles logout on the frontend, no backend endpoint needed + + +class ObtainInputFileView(APIView) : + def post(self , request) : + print('inside obtain all reports view post') + + # obtain the email + email = request.data.get('email') + print('email : ' , email) + fileIndex = request.data.get('fileIndex') + print('fileIndex : ' , fileIndex) + + userObject = UserAccount.objects.get(email = email) + filePath = userObject.allInputValueFiles[int(fileIndex)] + print('filePath : ' , filePath) + + try : + # send the input value files to the client + currentDirectory = os.getcwd() + print('current Directory : ' , currentDirectory) + fullpath = currentDirectory + "/file_storage/input_values_files/" + response = FileResponse(open(filePath, 'rb')) + response['Content-Type'] = 'text/plain' + response['Content-Disposition'] = f'attachment; filename="{filePath}"' + for key, value in response.items(): + print(f'{key}: {value}') + + return response + + except Exception as e : + print('An exception has occured in obtaining the osi file : ' , e) + + return Response({'message' : 'Inside obtain all report view'} , status = status.HTTP_500_INTERNAL_SERVER_ERROR) + + def get(self, request) : + print('inside obtain input file GET') + + print('request : ' , request) + print('request.GET : ' , request.GET) + fileName = request.GET.get('filename') + print('fileName obtained : ' , fileName) + filePath = os.path.join(os.getcwd(), "file_storage/input_values_files/" + fileName) + + try : + print('preparing download...') + response = FileResponse(open(filePath , 'rb')) + response['Content-Type'] = 'text/plain' + response['Content-Disposition'] = f'attachment; filename="{fileName}"' + for key, value in response.items() : + print(f'{key} : {value}') + + return response + + except Exception as e : + print('Exception in downloading : ' , e) + + return Response({'message' : 'Cannot download the file'} , status=status.HTTP_400_BAD_REQUEST) + +class SaveInputFileView(APIView) : + def post(self, request) : + print('inside the saveinput file view post') + + # New implementation: accept multipart upload of .osi file and store via OsiFile model + try: + uploaded_file = request.FILES.get('file') + if not uploaded_file: + return Response({'message': 'No file provided under field "file"'}, status=status.HTTP_400_BAD_REQUEST) + + serializer = OsiFileSerializer(data={'file': uploaded_file}) + if serializer.is_valid(): + osifile = serializer.save() + file_url = osifile.file.url + return Response({'message': 'File stored successfully', 'url': file_url, 'id': osifile.id}, status=status.HTTP_201_CREATED) + else: + return Response({'message': 'Invalid file upload', 'errors': serializer.errors}, status=status.HTTP_400_BAD_REQUEST) + except Exception as e: + print('Error saving OSI file via OsiFile model: ', e) + return Response({'message': 'Failed to store the file'}, status=status.HTTP_400_BAD_REQUEST) + + +# Removed: SetRefreshTokenCookieView - No longer needed with Firebase authentication diff --git a/backend/apps/core/api/cad/__init__.py b/backend/apps/core/api/cad/__init__.py new file mode 100644 index 000000000..c8a78bbf9 --- /dev/null +++ b/backend/apps/core/api/cad/__init__.py @@ -0,0 +1,10 @@ +""" +CAD Model APIs +""" +from .cad_model_api import CADGeneration +from .cad_model_download import CADDownload + +__all__ = [ + 'CADGeneration', 'CADDownload', +] + diff --git a/backend/apps/core/api/cad/cad_model_api.py b/backend/apps/core/api/cad/cad_model_api.py new file mode 100644 index 000000000..793db2002 --- /dev/null +++ b/backend/apps/core/api/cad/cad_model_api.py @@ -0,0 +1,348 @@ +""" + This file includes the CAD Model Generation and CAD Conversion API + Update input values in database. + CAD Model API (class CADGeneration(View)): + Accepts GET requests. + Saves obj file as output in frontend/public/output-obj.obj + Returns ouput dir name as content_type text/plain. + Request must provide session cookie id. +""" +from django.http import HttpResponse, JsonResponse, HttpRequest +from django.views import View +from apps.core.models import Design +from django.views.decorators.csrf import csrf_exempt +from django.utils.decorators import method_decorator +from apps.core.module_finder import developed_modules, get_module_api +import shutil +import json +import os +import subprocess +from io import BytesIO + +# rest_framework +from rest_framework import status +from rest_framework.response import Response + +@method_decorator(csrf_exempt, name='dispatch') +class CADGeneration(View): + """ + CAD Model API (class CADGeneration(View)): + Accepts POST requests with input data and module information. + Generates CAD models directly from input data without requiring sessions. + """ + + def post(self, request: HttpRequest): + try: + # Parse JSON request body + import json + request_data = json.loads(request.body) + + # Get module information and input values from request + module_id = request_data.get('module_id') + input_values = request_data.get('input_values') + + if not module_id: + return JsonResponse({"status": "error", "message": "module_id is required"}, status=400) + + if not input_values: + return JsonResponse({"status": "error", "message": "input_values are required"}, status=400) + + # Normalize hyphenated ids to backend ids + module_aliases = { + # legacy hyphenated + "ButtJointWelded": "ButtJointWelded", + "ButtJointBolted": "ButtJointBolted", + "LapJointWelded": "LapJointWelded", + "LapJointBolted": "LapJointBolted", + "Beam-to-Beam-Cover-Plate-Bolted-Connection": "Cover-Plate-Bolted-Connection", + "Beam-to-Beam-Cover-Plate-Welded-Connection": "Cover-Plate-Welded-Connection", + "Beam-Beam-End-Plate-Connection": "Beam-Beam-End-Plate-Connection", + "Beam-to-Column-End-Plate-Connection": "Beam-to-Column-End-Plate-Connection", + "Column-to-Column-Cover-Plate-Bolted-Connection": "ColumnCoverPlateBolted", + "Column-to-Column-Cover-Plate-Welded-Connection": "Column-to-Column-Cover-Plate-Welded-Connection", + "Column-to-Column-End-Plate-Connection": "Column-to-Column-End-Plate-Connection", + # slug forms + "butt-joint-welded": "ButtJointWelded", + "butt-joint-bolted": "ButtJointBolted", + "lap-joint-welded": "LapJointWelded", + "lap-joint-bolted": "LapJointBolted", + # shear slugs + "shear-connection/fin-plate": "FinPlateConnection", + "shear-connection/cleat-angle": "CleatAngleConnection", + "shear-connection/end-plate": "EndPlateConnection", + "shear-connection/seated-angle": "SeatedAngleConnection", + # moment slugs + "moment-connection/beam-beam-cover-plate-bolted": "Cover-Plate-Bolted-Connection", + "moment-connection/beam-beam-cover-plate-welded": "Cover-Plate-Welded-Connection", + "moment-connection/beam-beam-end-plate": "Beam-Beam-End-Plate-Connection", + "moment-connection/beam-column-end-plate": "Beam-to-Column-End-Plate-Connection", + "moment-connection/column-column-cover-plate-bolted": "ColumnCoverPlateBolted", + "moment-connection/column-column-cover-plate-welded": "Column-to-Column-Cover-Plate-Welded-Connection", + "moment-connection/column-column-end-plate": "Column-to-Column-End-Plate-Connection", + # moment keys + "CoverPlateBolted": "Cover-Plate-Bolted-Connection", + "CoverPlateWelded": "Cover-Plate-Welded-Connection", + "BeamBeamEndPlate": "Beam-Beam-End-Plate-Connection", + "BeamColumnEndPlate": "Beam-to-Column-End-Plate-Connection", + "CCCoverPlateBolted": "ColumnCoverPlateBolted", + "CCCoverPlateWelded": "Column-to-Column-Cover-Plate-Welded-Connection", + "CCEndPlate": "Column-to-Column-End-Plate-Connection", + # simple slugs already mapped above + } + canonical_module_id = module_aliases.get(module_id, module_id) + + # Get module API + module_api = get_module_api(canonical_module_id) + + # Determine session type from canonical_module_id + module_type_mapping = { + "FinPlateConnection": "FinPlateConnection", + "CleatAngleConnection": "CleatAngle", + "EndPlateConnection": "EndPlate", + "SeatedAngleConnection": "SeatedAngleConnection", + "Cover-Plate-Bolted-Connection": "CoverPlateBolted", + "Beam-Beam-End-Plate-Connection": "BeamBeamEndPlate", + "Cover-Plate-Welded-Connection": "CoverPlateWelded", + "Beam-to-Column-End-Plate-Connection": "BeamToColumnEndPlate", + "ColumnCoverPlateBolted": "ColumnCoverPlateBolted", + "Column-to-Column-Cover-Plate-Welded-Connection": "ColumnCoverPlateWelded", + "Column-to-Column-End-Plate-Connection": "ColumnEndPlate", + "Tension-Member-Bolted-Design": "TensionMember", + "Tension-Member-Welded-Design": "TensionMember", + "ButtJointWelded": "ButtJointWelded", + "ButtJointBolted": "ButtJointBolted", + "LapJointWelded": "LapJointWelded", + "LapJointBolted": "LapJointBolted", + } + print("module_type_mapping: ", module_type_mapping) + print("canonical_module_id: ", canonical_module_id) + session_type = module_type_mapping.get(canonical_module_id) + if not session_type: + return JsonResponse({"status": "error", "message": f"Unknown module type: {module_id}"}, status=400) + + except json.JSONDecodeError: + return JsonResponse({"status": "error", "message": "Invalid JSON in request body"}, status=400) + except Exception as e: + return JsonResponse({"status": "error", "message": f"Error parsing request: {str(e)}"}, status=500) + + # Directory setup + current_dir = os.path.dirname(os.path.abspath(__file__)) + # repo_root points to project root (.../Osdag-web) + repo_root = os.path.abspath(os.path.join(current_dir, "../../../../../")) + backend_root = os.path.join(repo_root, "backend") + + # Determine sections based on the session type and what each backend module expects + if session_type == "FinPlateConnection": + sections = ["Beam", "Column", "Plate"] + elif session_type == "CleatAngle": + sections = ["Model", "Beam", "Column", "cleatAngle"] + elif session_type == "EndPlate": + sections = ["Model", "Beam", "Column", "Plate"] + elif session_type == "SeatedAngleConnection": + sections = ["Model", "Beam", "Column", "SeatedAngle"] + elif session_type == "CoverPlateBolted": + sections = ["Model", "Beam", "CoverPlate"] + elif session_type == "BeamBeamEndPlate": + sections = ["Model", "Beam", "EndPlate"] + elif session_type == "CoverPlateWelded": + sections = ["Model", "Beam", "CoverPlate"] + elif session_type == "BeamToColumnEndPlate": + sections = ["Model", "Beam", "Column", "Connector"] + elif session_type == "ColumnCoverPlateBolted": + sections = ["Model", "Column", "CoverPlate"] + elif session_type == "ColumnCoverPlateWelded": + sections = ["Model", "Column", "CoverPlate"] + elif session_type == "ColumnEndPlate": + sections = ["Model", "Column", "Connector"] + elif session_type == "TensionMember": + sections = ["Model", "Member", "Plate", "Endplate"] + elif session_type in ("ButtJointWelded", "ButtJointBolted", "LapJointWelded", "LapJointBolted"): + sections = ["Model", "Column", "Plate"] + else: + return JsonResponse({"status": "error", "message": "Unknown module type"}, status=400) + + print(f"[cadissue] cad_model_api: module_id={module_id}, session_type={session_type}, sections={sections}") + + # initialize the empty dictionary to hold model data and collect errors + output_files = {} + error_details = [] + print("Design sections: ", sections) + + # Generate a unique session identifier for this CAD generation + import uuid + session_id = str(uuid.uuid4()) + + for section in sections: + print(f"[cadissue] Generating section: {section}") + try: + if not hasattr(module_api, 'create_cad_model'): + error_details.append({"section": section, "error": "create_cad_model not implemented"}) + continue + print(f"[cad_model_api] Calling create_cad_model for section '{section}' with session_id={session_id}") + path = module_api.create_cad_model(input_values, section, session_id) + + if not path: + print(f'[cad_model_api] Error generating {section}: create_cad_model() returned None or empty string') + continue # Skip to the next section + + print(f'[cad_model_api] {section} generated successfully, returned path: {path}') + print(f"[cadissue] create_cad_model returned path for section={section}: {path}") + + # Resolve returned path. Modules typically return "file_storage/..." + base_root = repo_root + if os.path.isabs(path): + path_to_file = path + else: + # Prefer project root; fall back to backend root (where CAD generators write) + path_repo_root = os.path.join(repo_root, path) + path_backend_root = os.path.join(backend_root, path) + if os.path.exists(path_repo_root): + path_to_file = path_repo_root + elif os.path.exists(path_backend_root): + path_to_file = path_backend_root + base_root = backend_root + else: + # Default to repo root for error reporting + path_to_file = path_repo_root + print(f"[cad_model_api] Resolved path for section '{section}': {path_to_file} (base_root={base_root})") + print(f"[cadissue] Path resolution for section={section}: {path_to_file}, exists={os.path.exists(path_to_file)}") + if not os.path.exists(path_to_file): + msg = f'Generated file for {section} does not exist at: {path_to_file}' + print(msg) + error_details.append({"section": section, "error": msg}) + continue + + stl_path = path_to_file.replace(".brep", ".stl") + print(f"[cadissue] Looking for STL for section={section} at: {stl_path}") + import base64 + try: + if os.path.exists(stl_path): + print(f"[cad_model_api] Using STL for section '{section}': {stl_path}") + with open(stl_path, "rb") as f: + b64 = base64.b64encode(f.read()).decode("ascii") + output_files[section] = f"data:application/octet-stream;base64,{b64}" + print(f"[cad_model_api] Loaded STL for {section}") + print(f"[cadissue] Stored STL entry for section={section}") + else: + print(f"[cad_model_api] STL missing for section '{section}', falling back to BREP: {path_to_file}") + with open(path_to_file, "rb") as f: + b64 = base64.b64encode(f.read()).decode("ascii") + output_files[section] = f"data:application/octet-stream;base64,{b64}" + print(f"[cad_model_api] Loaded BREP for {section}") + print(f"[cadissue] Stored BREP entry for section={section}") + + # If this is the merged Model, also try to include per-part STLs from manifest + if section == "Model": + try: + manifest_path = path_to_file.replace(".brep", ".parts.json") + if os.path.exists(manifest_path): + print(f"[cad_model_api] Found manifest at {manifest_path}") + import json as _json + with open(manifest_path, "r", encoding="utf-8") as mf: + manifest = _json.load(mf) + parts = manifest.get("parts", []) + print(f"[cad_model_api] Manifest parts count: {len(parts)}") + for entry in parts: + name = entry.get("name") + stl_rel = entry.get("stlPath") + brep_rel = entry.get("brepPath") + if not name: + continue + # Prefer STL + part_file_abs = None + part_base_roots = [base_root, repo_root] + if stl_rel: + for root in part_base_roots: + candidate = os.path.join(root, stl_rel) + if os.path.exists(candidate): + part_file_abs = candidate + print(f"[cad_model_api] Part '{name}' using STL {candidate}") + break + if not part_file_abs and brep_rel: + for root in part_base_roots: + candidate = os.path.join(root, brep_rel) + if os.path.exists(candidate): + part_file_abs = candidate + print(f"[cad_model_api] Part '{name}' using BREP {candidate}") + break + if part_file_abs: + # Prefer manifest part files for accuracy, even if section already populated + with open(part_file_abs, "rb") as pf: + b64p = base64.b64encode(pf.read()).decode("ascii") + output_files[name] = f"data:application/octet-stream;base64,{b64p}" + print(f"[cad_model_api] Loaded part {name} from manifest (overrides section if existed)") + except Exception as me: + print(f"Failed to load parts from manifest: {me}") + except Exception as e: + print(f"Failed reading model file for {section}: {e}") + error_details.append({"section": section, "error": f"Failed reading file: {str(e)}"}) + continue + + except Exception as e: + print(f"Exception while generating {section}: {e}") + error_details.append({"section": section, "error": str(e)}) + + print(f"[cad_model_api] Final output_files keys: {list(output_files.keys())}") + print(f"[cadissue] Final output_files keys: {list(output_files.keys())}") + + if not output_files: + # Unprocessable due to inputs or environment; include details to aid debugging + return JsonResponse({"status": "error", "message": "No CAD models were generated.", "errors": error_details}, status=422) + + # Build hover_dict if possible using module APIs (e.g., FinPlateConnection) + # hover_dict is populated in output_details() which is called from output_values() + hover_dict = {} + try: + if hasattr(module_api, 'create_from_input') and callable(module_api.create_from_input): + mdl = module_api.create_from_input(input_values) + + # Call output_values() to populate hover_dict (output_details is called internally) + # This ensures hover_dict is populated before we try to access it + if hasattr(mdl, 'output_values') and callable(mdl.output_values): + try: + # Call output_values with flag=True to populate hover_dict + mdl.output_values(True) + print(f"[cad_model_api] Called output_values(), hover_dict populated: {getattr(mdl, 'hover_dict', None)}") + except Exception as output_err: + print(f"[cad_model_api] Error calling output_values(): {output_err}") + import traceback + traceback.print_exc() + + # Now get hover_dict after it's been populated + cand = getattr(mdl, 'hover_dict', None) + if isinstance(cand, dict) and len(cand) > 0: + hover_dict = cand + print(f"[cad_model_api] Retrieved hover_dict: {hover_dict}") + else: + print(f"[cad_model_api] hover_dict is empty or not a dict: {cand}") + # Minimal fallback for Bolt info when detailed dict is not available + bolt_grade = None + bolt_dia = None + try: + grades = input_values.get('Bolt.Grade') or [] + dias = input_values.get('Bolt.Diameter') or [] + if isinstance(grades, list) and grades: + bolt_grade = grades[-1] + if isinstance(dias, list) and dias: + bolt_dia = dias[-1] + except Exception: + pass + if bolt_grade or bolt_dia: + parts = [] + if bolt_grade: + parts.append(f"Grade: {bolt_grade}") + if bolt_dia: + parts.append(f"Diameter: {bolt_dia} mm") + hover_dict['Bolt'] = ' '.join(parts) + except Exception as _e: + # Log the error but don't fail the request + print(f"[cad_model_api] Error building hover_dict: {_e}") + import traceback + traceback.print_exc() + return JsonResponse({ + "status": "success", + "files": output_files, + "message": "CAD models generated successfully", + "warnings": error_details, # include any partial failures + "hover_dict": hover_dict + }, status=201) diff --git a/backend/apps/core/api/cad/cad_model_download.py b/backend/apps/core/api/cad/cad_model_download.py new file mode 100644 index 000000000..6c860e5be --- /dev/null +++ b/backend/apps/core/api/cad/cad_model_download.py @@ -0,0 +1,44 @@ +from rest_framework.views import APIView +from rest_framework.parsers import JSONParser +from django.http import FileResponse, JsonResponse +import os +import uuid + +class CADDownload(APIView): + def post(self, request): + try: + # Parse format, section, and request_id from JSON body + # data = JSONParser().parse(request) + data = request.data + format_type = data.get('format') + section = data.get('section') + request_id = data.get('request_id') # Use request_id instead of session + + # If no request_id provided, generate a unique one for this request + if not request_id: + request_id = str(uuid.uuid4()) + + if not format_type or not section: + return JsonResponse({"status": "error", "message": "Missing required parameters."}, status=400) + + allowed_formats = ["obj", "brep", "step", "iges"] + if format_type.lower() not in allowed_formats: + return JsonResponse({"status": "error", "message": "Invalid format type requested."}, status=400) + + # Path setup + if format_type == "obj": + file_path = os.path.join("frontend", "public", f"output-{section.lower()}.obj") + else: + file_path = os.path.join("file_storage", "cad_models", f"{request_id}_{section}.{format_type.lower()}") + + if not os.path.exists(file_path): + return JsonResponse({"status": "error", "message": f"{format_type.upper()} file does not exist."}, status=404) + + # Serve file + response = FileResponse(open(file_path, 'rb'), content_type='application/octet-stream') + response['Content-Disposition'] = f'attachment; filename="{request_id}_{section}.{format_type}"' + return response + + except Exception as e: + print(f"Download CAD error: {e}") + return JsonResponse({"status": "error", "message": "Internal server error"}, status=500) diff --git a/backend/apps/core/api/design/__init__.py b/backend/apps/core/api/design/__init__.py new file mode 100644 index 000000000..eecd018f7 --- /dev/null +++ b/backend/apps/core/api/design/__init__.py @@ -0,0 +1,13 @@ +""" +Design & Reporting APIs +""" +from .design_pref_api import DesignPreference, MaterialDetails +from .design_report_csv_view import CompanyLogoView, CreateDesignReport, GetPDF +from .report_customization_api import ParseReportSections, CustomizeReport + +__all__ = [ + 'DesignPreference', 'MaterialDetails', 'CompanyLogoView', + 'CreateDesignReport', 'GetPDF', + 'ParseReportSections', 'CustomizeReport', +] + diff --git a/backend/apps/core/api/design/design_pref_api.py b/backend/apps/core/api/design/design_pref_api.py new file mode 100644 index 000000000..c52d4c0cc --- /dev/null +++ b/backend/apps/core/api/design/design_pref_api.py @@ -0,0 +1,84 @@ +from rest_framework.permissions import AllowAny +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from apps.core.models import Design +from apps.core.models import Beams +from apps.core.models import Columns +from apps.core.models import Material, CustomMaterials +from apps.core.serializers import Material_Serializer, CustomMaterials_Serializer + + +class DesignPreference(APIView): + permission_classes = [AllowAny] + + def get(self, request): + supported_section = request.GET.get("supported_section") + supporting_section = request.GET.get("supporting_section") + connectivity = request.GET.get("connectivity") + material = request.GET.get("material") + # Session validation removed - now stateless + + connector_material_details = [] + if material: + connector_material_details = Material.objects.filter(Grade=material).values() + return Response({"connector_material_details": connector_material_details }, status=status.HTTP_200_OK) + + + if connectivity == 'Beam-Beam': + supported_section_results = Beams.objects.filter(Designation=supported_section).values() + supporting_section_results = Beams.objects.filter(Designation=supporting_section).values() + elif not connectivity: + supported_section_results = Beams.objects.filter(Designation=supported_section).values() + return Response({"supported_section_results": supported_section_results}, status=status.HTTP_200_OK) + else : + supported_section_results = Beams.objects.filter(Designation=supported_section).values() + supporting_section_results = Columns.objects.filter(Designation=supporting_section).values() + + return Response({"supported_section_results": supported_section_results, "supporting_section_results":supporting_section_results}, status=status.HTTP_200_OK) + + +class MaterialDetails(APIView): + + def get(self, request): + email = request.GET.get("email") + material = request.GET.get("material") + # Session validation removed - now stateless + + if email: + custom_materials = CustomMaterials.object.filter(email=email).values() + + material_details = Material.objects.filter(Grade=material).values() + + total_materials = custom_materials + material_details + + return Response({"material_details": material_details }, status=status.HTTP_200_OK) + + def post(self, request): + email = request.data.get("email") + materialName = request.data.get("materialName") + fy_20 = request.data.get("fy_20") + fy_20_40 = request.data.get("fy_20_40") + fy_40 = request.data.get("fy_40") + fu = request.data.get("fu") + # Session validation removed - now stateless + + alreadyExists = CustomMaterials.objects.filter(email=email, Grade=materialName).exists() + if alreadyExists: + return Response({"message": "The material already exists", "success": False}, status=403) + + serializer = CustomMaterials_Serializer(data = { + "email": email, + "Grade": materialName, + "Yield_Stress_less_than_20": fy_20, + "Yield_Stress_between_20_and_neg40": fy_20_40, + "Yield_Stress_greater_than_40": fy_40, + "Ultimate_Tensile_Stress": fu, + }) + + if serializer.is_valid(): + serializer.save() + return Response({"message" : "Material added successfuly", "success": True} , status=201) + else: + print('serializer.errors : ' , serializer.errors) + return Response({"message" : "Something went wrong", "success": True} , status=500) diff --git a/backend/apps/core/api/design/design_report_csv_view.py b/backend/apps/core/api/design/design_report_csv_view.py new file mode 100644 index 000000000..201f73059 --- /dev/null +++ b/backend/apps/core/api/design/design_report_csv_view.py @@ -0,0 +1,466 @@ +from rest_framework.response import Response +from rest_framework import status +from rest_framework.views import APIView +from rest_framework.permissions import AllowAny +from django.utils.crypto import get_random_string +from django.http import FileResponse +from django.views.decorators.csrf import csrf_exempt +from django.utils.decorators import method_decorator + +from apps.modules.shear_connection.submodules.fin_plate.adapter import create_from_input as fin_plate_create_from_input +from apps.modules.shear_connection.submodules.end_plate.adapter import create_from_input as end_plate_create_from_input +from apps.modules.shear_connection.submodules.cleat_angle.adapter import create_from_input as cleat_angle_create_from_input +from apps.modules.shear_connection.submodules.seated_angle.adapter import create_from_input as seated_angle_create_from_input +from apps.modules.moment_connection.submodules.beam_beam_cover_plate_bolted.adapter import create_from_input as cover_plate_bolted_create_from_input +from apps.modules.moment_connection.submodules.beam_beam_cover_plate_welded.adapter import create_from_input as cover_plate_welded_create_from_input +from apps.modules.moment_connection.submodules.beam_beam_end_plate.adapter import create_from_input as beam_beam_end_plate_create_from_input +from apps.modules.moment_connection.submodules.beam_column_end_plate.adapter import create_from_input as beam_to_column_end_plate_create_from_input +from apps.modules.moment_connection.submodules.column_column_cover_plate_bolted.adapter import create_from_input as column_cover_plate_bolted_create_from_input +from apps.modules.moment_connection.submodules.column_column_cover_plate_welded.adapter import create_from_input as column_cover_plate_welded_create_from_input +from apps.modules.moment_connection.submodules.column_column_end_plate.adapter import create_from_input as column_end_plate_create_from_input +from apps.modules.tension_member.submodules.bolted.adapter import create_from_input as tension_member_bolted_create_from_input +from apps.modules.tension_member.submodules.welded.adapter import create_from_input as tension_member_welded_create_from_input +# importing models +from apps.core.models import Design + +from django.core.files.storage import default_storage + + +# DRF imports +from rest_framework.parsers import MultiPartParser , FormParser +from rest_framework import status + + +# other imports +import os +import platform +import subprocess +import json +import time +import uuid +import re +import base64 + +# Helper: filter LaTeX content based on selected sections (section or section/subsection) +def filter_latex_content(latex_content: str, selected_sections): + if not selected_sections or not isinstance(selected_sections, (list, tuple)): + return latex_content + + lines = latex_content.split('\n') + filtered_lines = [] + current_section = None + current_subsection = None + include_content = True + section_started = False + + for line in lines: + section_match = re.search(r'\\section\{([^}]+)\}', line) + if section_match: + current_section = section_match.group(1).strip() + current_subsection = None + section_started = True + include_content = ( + current_section in selected_sections or + any(sel.startswith(f"{current_section}/") for sel in selected_sections) + ) + + subsection_match = re.search(r'\\subsection\{([^}]+)\}', line) + if subsection_match and current_section: + current_subsection = subsection_match.group(1).strip() + subsection_key = f"{current_section}/{current_subsection}" + include_content = ( + current_section in selected_sections or + subsection_key in selected_sections + ) + + if ( + include_content or + not section_started or + line.startswith('\\documentclass') or + line.startswith('\\usepackage') or + line.startswith('\\title') or + line.startswith('\\author') or + line.startswith('\\date') or + line.startswith('\\begin{document}') or + line.startswith('\\maketitle') or + line.startswith('\\end{document}') + ): + filtered_lines.append(line) + + return '\n'.join(filtered_lines) + +@method_decorator(csrf_exempt, name='dispatch') +class CreateDesignReport(APIView): + permission_classes = [AllowAny] + + def dispatch(self, request, *args, **kwargs): + """Override dispatch to log all requests, even if blocked by permissions""" + try: + result = super().dispatch(request, *args, **kwargs) + return result + except Exception as e: + raise + + def post(self, request): + # Get metadata and design data from request + metadata = request.data.get('metadata') + module_id = request.data.get('module_id') + input_values = request.data.get('input_values') + design_status = request.data.get('design_status', True) + logs = request.data.get('logs', []) + # Optional: sections to include (from UI customization popup) and other customization + sections = request.data.get('sections') # e.g., ["Introduction", "Inputs", "Outputs/Spacing"] + customization = request.data.get('customization') # generic dict for future options + + # Optional: pre-rendered images from frontend (data URLs or base64) + images = request.data.get('images') or {} + + # Map module IDs to their respective create_from_input functions + module_function_map = { + 'FinPlateConnection': fin_plate_create_from_input, + 'EndPlateConnection': end_plate_create_from_input, + 'CleatAngleConnection': cleat_angle_create_from_input, + 'Seated-Angle-Connection': seated_angle_create_from_input, + 'Cover-Plate-Bolted-Connection': cover_plate_bolted_create_from_input, + 'Beam-Beam-End-Plate-Connection': beam_beam_end_plate_create_from_input, + 'Cover-Plate-Welded-Connection': cover_plate_welded_create_from_input, + 'Beam-to-Column-End-Plate-Connection': beam_to_column_end_plate_create_from_input, + 'ColumnCoverPlateBolted': column_cover_plate_bolted_create_from_input, + 'Column-to-Column-Cover-Plate-Welded-Connection': column_cover_plate_welded_create_from_input, + 'Column-to-Column-End-Plate-Connection': column_end_plate_create_from_input, + 'Tension-Member-Bolted-Design': tension_member_bolted_create_from_input, + 'Tension-Member-Welded-Design': tension_member_welded_create_from_input + } + + if not module_id or module_id not in module_function_map: + return Response({"error": "Invalid or missing module_id"}, status=status.HTTP_400_BAD_REQUEST) + + if not input_values: + return Response({"error": "Missing input_values"}, status=status.HTTP_400_BAD_REQUEST) + + create_module_func = module_function_map[module_id] + + # obtain the currenct working directory as it gets changed in the osdag desktop code, then + # we will use the same value to bring it back to the current directory + current_directory = os.getcwd() + if (metadata is None or metadata == ''): + metadata_profile = { + "CompanyName": "Your Company", + "CompanyLogo": "", + "Group/TeamName": "Your Team", + "Designer": "You" + } + + metadata_other = { + "ProjectTitle": "Osdag", + "Subtitle": "", + "JobNumber": "1", + "AdditionalComments": "No Comments", + "Client": "Someone else", + } + # generate a random string for report id + report_id = get_random_string(length=16) + # Each report gets its own folder: + # file_storage/design_report/{report_id}/{report_id}.tex + report_root = os.path.join(os.getcwd(), "file_storage", "design_report", report_id) + os.makedirs(report_root, exist_ok=True) + file_path = os.path.join(report_root, report_id) + + # append the file path in the meta data + metadata_final = { + "ProfileSummary": metadata_profile, + "filename": file_path, + } + for key in metadata_other.keys(): + metadata_final[key] = metadata_other[key] + + metadata_final['does_design_exist'] = design_status + # Convert logs to string format for LaTeX + if logs and isinstance(logs, list): + logger_string = '\n'.join([f"{log.get('timestamp', '')} - {log.get('type', 'INFO')} - {log.get('message', '')}" for log in logs]) + else: + logger_string = "No logs available" + metadata_final['logger_messages'] = logger_string + # Attach optional UI customization + if sections: + metadata_final['selected_sections'] = sections + if customization: + metadata_final['customization'] = customization + + else : + # generate a random string for report id + report_id = get_random_string(length=16) + # Each report gets its own folder: + # file_storage/design_report/{report_id}/{report_id}.tex + report_root = os.path.join(os.getcwd(), "file_storage", "design_report", report_id) + os.makedirs(report_root, exist_ok=True) + file_path = os.path.join(report_root, report_id) + metadata_final = metadata + metadata_final['does_design_exist'] = design_status + # Convert logs to string format for LaTeX + if logs and isinstance(logs, list): + logger_string = '\n'.join([f"{log.get('timestamp', '')} - {log.get('type', 'INFO')} - {log.get('message', '')}" for log in logs]) + else: + logger_string = "No logs available" + metadata_final['logger_messages'] = logger_string + metadata_final['filename'] = file_path + # Attach optional UI customization + if sections: + metadata_final['selected_sections'] = sections + if customization: + metadata_final['customization'] = customization + + # check if the design_report root folder has been created or not + # if not, create one + cwd = os.path.join(os.getcwd(), "file_storage", "design_report") + os.makedirs(cwd, exist_ok=True) + + try: + module = create_module_func(input_values) + except Exception as e: + import traceback + traceback.print_exc() + raise + + # ------------------------------------------------------------ + # Normalize metadata/logs for legacy save_design() + # The desktop report code expects string fields and may call + # .split() on them; make sure we don't pass lists/dicts. + # ------------------------------------------------------------ + try: + raw_logs = metadata_final.get('logs') + if isinstance(raw_logs, list): + normalized_lines = [] + for log in raw_logs: + if isinstance(log, dict): + ts = log.get('timestamp', '') + lvl = log.get('type', 'INFO') + msg = log.get('message', '') + normalized_lines.append(f"{ts} - {lvl} - {msg}") + else: + normalized_lines.append(str(log)) + metadata_final['logs'] = "\n".join(normalized_lines) + + # Ensure logger_messages is a plain string + if isinstance(metadata_final.get('logger_messages'), list): + metadata_final['logger_messages'] = "\n".join( + str(x) for x in metadata_final['logger_messages'] + ) + except Exception as norm_exc: + # Don't fail report generation just because normalization failed; + # log and continue with original metadata. + pass + + from osdag_core.Common import KEY_DISP_FINPLATE, KEY_DISP_ENDPLATE + + # Fix module.module if it doesn't match KEY_DISP_FINPLATE or KEY_DISP_ENDPLATE + # This is needed for parent save_design() to set report_input + if hasattr(module, 'module'): + if module.module == 'FinPlateConnection' and module_id == 'FinPlateConnection': + module.module = KEY_DISP_FINPLATE + elif module.module == 'EndPlateConnection' and module_id == 'EndPlateConnection': + module.module = KEY_DISP_ENDPLATE + + # Check if design has been run - output_values() typically triggers design + # But for report generation, we need to ensure design is complete + try: + # Try calling output_values to trigger design if not already done + if not getattr(module, 'design_status', False): + try: + _ = module.output_values(True) + except Exception: + pass + except Exception: + pass + + # ------------------------------------------------------------------ + # Image handling for reports (frontend-driven only) + # ------------------------------------------------------------------ + # If the frontend sent pre-rendered images, save them to the + # per-report directory alongside the .tex/.pdf. We no longer invoke + # server-side CAD image generation for web reports; images are + # purely frontend-driven. + try: + if isinstance(images, dict) and images: + report_base_dir = os.path.dirname(file_path) + image_base_dir = os.path.join(report_base_dir, "ResourceFiles", "images") + os.makedirs(image_base_dir, exist_ok=True) + + # Map canonical frontend keys to the filenames expected by + # existing LaTeX templates. + filename_map = { + "iso": "3d.png", # 3D/iso view + "3d": "3d.png", + "front": "front.png", + "side": "side.png", + "top": "top.png", + } + + for key, data_url in images.items(): + try: + if not isinstance(data_url, str): + continue + # Support both full data URLs and raw base64 + if ',' in data_url and data_url.strip().lower().startswith('data:'): + _, b64 = data_url.split(',', 1) + else: + b64 = data_url + img_bytes = base64.b64decode(b64) + + normalized_key = str(key).lower() + filename = filename_map.get(normalized_key, f"{key}.png") + out_path = os.path.join(image_base_dir, filename) + with open(out_path, "wb") as img_f: + img_f.write(img_bytes) + except Exception as img_exc: + import logging + logging.getLogger(__name__).error( + "Failed to save report image %s: %s", key, img_exc + ) + except Exception: + # Image handling is best-effort; ignore failures + pass + + try: + resultBoolean = module.save_design(metadata_final) + except Exception as e: + # Legacy desktop save_design sometimes raises even after writing .tex, + # for example when it calls .split() on an internal list. + # Treat this as non-fatal if the expected LaTeX file was created. + import traceback + traceback.print_exc() + + tex_path = f'{file_path}.tex' + if os.path.exists(tex_path): + resultBoolean = True + else: + resultBoolean = False # Set default value if save_design fails and no file + + os.chdir(current_directory) + + if (resultBoolean): + time.sleep(3) + # If sections provided, post-filter LaTeX file to include only selected sections + try: + if sections: + tex_path = f'{file_path}.tex' + with open(tex_path, 'r', encoding='utf-8') as rf: + original_tex = rf.read() + filtered_tex = filter_latex_content(original_tex, sections) + with open(tex_path, 'w', encoding='utf-8') as wf: + wf.write(filtered_tex) + except Exception: + pass + + # open and read the file contents (for debug only) + tex_path = f'{file_path}.tex' + f = open(tex_path, 'rb') + + return Response({'success': 'Design report created', 'report_id': report_id, 'fileContents : ': f}, status=status.HTTP_201_CREATED) + + elif(not resultBoolean): + return Response({"message" : "Error in generating the design report"}) + + +class GetPDF(APIView): + + def get(self, request): + # obtain the param from the Query + report_id = request.GET.get('report_id') + + # TeX source filename (inside the per-report folder) + report_root = os.path.join(os.getcwd(), "file_storage", "design_report", report_id) + tex_filename = f'{report_id}.tex' + filename, ext = os.path.splitext(tex_filename) + # the corresponding PDF filename (same basename, in the same folder) + pdf_filename = filename + '.pdf' + + # change the working directory to the per-report folder so pdflatex + # reads/writes next to the .tex file + path = report_root + os.chdir(path) + pdfFilePath = os.path.join(path, pdf_filename) + + # compile TeX file for different operating systems + if platform.system().lower() == 'windows': + subprocess.run(['cmd', '/c', 'echo', '%cd%']) + subprocess.run( + ['pdflatex', '-interaction=nonstopmode', tex_filename]) + else: + subprocess.run(['pwd']) + subprocess.run( + ['pdflatex', '-interaction=nonstopmode', tex_filename]) + + # check if PDF is successfully generated + if not os.path.exists(pdfFilePath): + raise RuntimeError('PDF output not found') + + # open PDF with platform-specific command + if platform.system().lower() == 'darwin': + subprocess.run(['open', pdfFilePath]) + elif platform.system().lower() == 'windows': + os.startfile(pdfFilePath) + elif platform.system().lower() == 'linux': + subprocess.run(['xdg-open', pdfFilePath]) + else: + raise RuntimeError( + 'Unknown operating system "{}"'.format(platform.system())) + + # delete the extra aux, log files, tex files generated in design_report + try: + # delete the following paths only when the pdf file is created + if os.path.exists(pdfFilePath): + base_no_ext = os.path.join(path, filename) + aux_path = f'{base_no_ext}.aux' + log_path = f'{base_no_ext}.log' + tex_path = f'{base_no_ext}.tex' + for p in (aux_path, log_path, tex_path): + try: + if os.path.exists(p): + os.remove(p) + except Exception: + pass + except Exception: + pass + + # Return the PDF file as a response + response = FileResponse(open(pdfFilePath, 'rb')) + response['Content-Type'] = 'application/pdf' + response['Content-Disposition'] = f'attachment; filename="{report_id}.pdf"' + return response + + +class CompanyLogoView(APIView) : + parser_classes = (MultiPartParser , FormParser) + + def post(self, request): + # check cookie + try: + cookie_id = request.COOKIES.get('fin_plate_connection_session') + except Exception: + pass + + # obtain the file + file = request.data['file'] + + # generate a unique name for the file + fileName = ''.join(str(uuid.uuid4()).split('-')) + ".png" + currentDirectory = os.getcwd() + + # create the png file + try : + with open(currentDirectory+"/file_storage/company_logo/"+fileName , 'w') as fp : + pass + except : + pass + + try : + with default_storage.open(currentDirectory+"/file_storage/company_logo/"+fileName, 'wb+') as destination : + for chunk in file.chunks() : + destination.write(chunk) + + # full path of the company logo w.r.t the Project + logoFullPath = currentDirectory+"/file_storage/company_logo/"+fileName + return Response({'message' : 'successfully saved file' , 'logoFullPath' : logoFullPath} , status = status.HTTP_201_CREATED) + except : + return Response({'message' : 'Error in saving the file'} , status = status.HTTP_400_BAD_REQUEST) diff --git a/backend/apps/core/api/design/report_customization_api.py b/backend/apps/core/api/design/report_customization_api.py new file mode 100644 index 000000000..046fd3b96 --- /dev/null +++ b/backend/apps/core/api/design/report_customization_api.py @@ -0,0 +1,524 @@ +""" +Report Customization API + +This module provides APIs for customizing design reports by allowing users to: +1. Parse sections from generated LaTeX reports +2. Filter content based on selected sections +3. Generate customized PDFs + +Author: AI Assistant +""" + +import os +import re +import tempfile +import subprocess +import shutil +import platform +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from rest_framework.permissions import AllowAny +from django.http import FileResponse +from django.core.files.storage import default_storage +from django.views.decorators.csrf import csrf_exempt +from django.utils.decorators import method_decorator + + +class LaTeXParser: + """ + Parses LaTeX files to extract document structure. + + This class finds all \section{} and \subsection{} commands in a LaTeX + document and organizes them into a hierarchical structure. + """ + + def parse_sections(self, latex_content): + """ + Extract sections and subsections from LaTeX content. + + Args: + latex_content (str): Raw LaTeX document content + + Returns: + dict: Hierarchical structure of sections and subsections + """ + sections = {} + current_section = None + + # Process each line to find LaTeX section commands + for line in latex_content.split('\n'): + # Look for \section{Section Name} patterns + section_match = re.search(r'\\section\{([^}]+)\}', line) + if section_match: + current_section = section_match.group(1).strip() + sections[current_section] = [] # Initialize subsection list + + # Look for \subsection{Subsection Name} patterns + subsection_match = re.search(r'\\subsection\{([^}]+)\}', line) + if subsection_match and current_section: + subsection = subsection_match.group(1).strip() + sections[current_section].append(subsection) + + return sections + + +class LaTeXFilter: + """ + Filters LaTeX content based on selected sections. + """ + + def filter_content(self, latex_content, selected_sections): + """ + Remove unselected sections from LaTeX content. + + Args: + latex_content (str): Original LaTeX content + selected_sections (list): List of selected sections/subsections + + Returns: + str: Filtered LaTeX content + """ + if not selected_sections or not isinstance(selected_sections, (list, tuple)): + return latex_content + + lines = latex_content.split('\n') + filtered_lines = [] + current_section = None + current_subsection = None + include_content = True + section_started = False + + for line in lines: + # Check for new section + section_match = re.search(r'\\section\{([^}]+)\}', line) + if section_match: + current_section = section_match.group(1).strip() + current_subsection = None + section_started = True + # Include section if it's selected OR if any of its subsections are selected + include_content = (current_section in selected_sections or + any(sel.startswith(f"{current_section}/") for sel in selected_sections)) + + # Check for subsection + subsection_match = re.search(r'\\subsection\{([^}]+)\}', line) + if subsection_match and current_section: + current_subsection = subsection_match.group(1).strip() + subsection_key = f"{current_section}/{current_subsection}" + # Include subsection only if specifically selected OR parent section is fully selected + include_content = (current_section in selected_sections or + subsection_key in selected_sections) + + # Always include document structure and preamble + if (include_content or + not section_started or # Include everything before first section + line.startswith('\\documentclass') or + line.startswith('\\usepackage') or + line.startswith('\\title') or + line.startswith('\\author') or + line.startswith('\\date') or + line.startswith('\\begin{document}') or + line.startswith('\\maketitle') or + line.startswith('\\end{document}')): + filtered_lines.append(line) + + return '\n'.join(filtered_lines) + + +@method_decorator(csrf_exempt, name='dispatch') +class ParseReportSections(APIView): + """ + API endpoint to parse sections from a generated LaTeX report. + + POST /api/report/parse-sections/ + Body: {"report_id": "string"} + """ + permission_classes = [AllowAny] + + def post(self, request): + try: + print('[report_customization_api] ParseReportSections:request', request.data) + report_id = request.data.get('report_id') + if not report_id: + return Response( + {"error": "report_id is required"}, + status=status.HTTP_400_BAD_REQUEST + ) + + # Check if LaTeX file exists + tex_file_path = os.path.join(os.getcwd(), 'file_storage', 'design_report', report_id, f'{report_id}.tex') + if not os.path.exists(tex_file_path): + return Response( + {"error": "LaTeX file not found. Please generate report first."}, + status=status.HTTP_404_NOT_FOUND + ) + + # Read LaTeX content + with open(tex_file_path, 'r', encoding='utf-8') as f: + latex_content = f.read() + + # Parse sections + parser = LaTeXParser() + sections = parser.parse_sections(latex_content) + + if not sections: + return Response( + {"error": "No sections found in LaTeX file"}, + status=status.HTTP_404_NOT_FOUND + ) + + response_payload = { + "success": True, + "sections": sections, + "report_id": report_id + } + print('[report_customization_api] ParseReportSections:response', { 'report_id': report_id, 'sections_keys': list(sections.keys()) }) + return Response(response_payload, status=status.HTTP_200_OK) + + except Exception as e: + return Response( + {"error": f"Failed to parse sections: {str(e)}"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + + +@method_decorator(csrf_exempt, name='dispatch') +class CustomizeReport(APIView): + """ + API endpoint to generate customized PDF with selected sections. + + POST /api/report/customize/ + Body: { + "report_id": "string", + "selected_sections": ["Section1", "Section2/Subsection1", ...] + } + """ + permission_classes = [AllowAny] + + def post(self, request): + try: + print('[report_customization_api] CustomizeReport:starting') + print('[report_customization_api] CustomizeReport:request', request.data) + report_id = request.data.get('report_id') + selected_sections = request.data.get('selected_sections', []) + print('[report_customization_api] CustomizeReport:parsed', {'report_id': report_id, 'selected_count': len(selected_sections)}) + + if not report_id: + return Response( + {"error": "report_id is required"}, + status=status.HTTP_400_BAD_REQUEST + ) + + # Check if LaTeX file exists + tex_file_path = os.path.join(os.getcwd(), 'file_storage', 'design_report', report_id, f'{report_id}.tex') + if not os.path.exists(tex_file_path): + return Response( + {"error": "LaTeX file not found. Please generate report first."}, + status=status.HTTP_404_NOT_FOUND + ) + + # Read original LaTeX content + print('[report_customization_api] CustomizeReport:reading file') + with open(tex_file_path, 'r', encoding='utf-8') as f: + original_latex = f.read() + print('[report_customization_api] CustomizeReport:file read', {'length': len(original_latex)}) + + # Filter content based on selected sections + print('[report_customization_api] CustomizeReport:creating filter') + filter_obj = LaTeXFilter() + print('[report_customization_api] CustomizeReport:filtering content') + filtered_latex = filter_obj.filter_content(original_latex, selected_sections) + filtered_latex = filtered_latex.replace( + r"\usepackage{lastpage}", + r"\usepackage{pageslts}" + ) + filtered_latex = filtered_latex.replace( + r"\pageref{LastPage}", + r"\lastpageref{pagesLTS.lastpage}" + ) + filtered_latex = ( + filtered_latex + .lstrip('\ufeff') + .replace('\r\n', '\n') + .replace('\r', '\n') + ) + print('[report_customization_api] CustomizeReport:filter', { 'report_id': report_id, 'selected_count': len(selected_sections) }) + print('[report_customization_api] CustomizeReport:filtered content length', len(filtered_latex)) + print('[report_customization_api] CustomizeReport:filtered content preview', filtered_latex[:200]) + + # Use fixed temp directory to overwrite each time (matching desktop behavior) + print('[report_customization_api] CustomizeReport:creating temp dir') + # safe_temp_dir = os.path.join(tempfile.gettempdir(), "osdag_pdf_compile") + safe_temp_dir = tempfile.mkdtemp(prefix="osdag_pdf_") + print('[report_customization_api] CustomizeReport:temp dir path', safe_temp_dir) + try: + if os.path.exists(safe_temp_dir): + print('[report_customization_api] CustomizeReport:removing existing temp dir') + shutil.rmtree(safe_temp_dir, ignore_errors=True) + print('[report_customization_api] CustomizeReport:creating temp dir') + os.makedirs(safe_temp_dir, exist_ok=True) + print('[report_customization_api] CustomizeReport:temp dir created successfully') + except Exception as e: + print('[report_customization_api] CustomizeReport:temp dir error', str(e)) + raise + + # Write filtered LaTeX to fixed directory + print('[report_customization_api] CustomizeReport:writing filtered tex') + custom_tex_path = os.path.join(safe_temp_dir, "filtered_report.tex") + print('[report_customization_api] CustomizeReport:custom tex path', custom_tex_path) + try: + print("custom_tex_path:", custom_tex_path) + print("parent dir exists:", os.path.exists(os.path.dirname(custom_tex_path))) + print("parent dir writable:", os.access(os.path.dirname(custom_tex_path), os.W_OK)) + with open(custom_tex_path, 'w', encoding='utf-8') as f: + f.write(filtered_latex) + print('[report_customization_api] CustomizeReport:filtered tex written successfully') + except Exception as e: + print('[report_customization_api] CustomizeReport:file write error', str(e)) + raise + + # Compile LaTeX to PDF + pdf_path = custom_tex_path.replace('.tex', '.pdf') + + # Change to temp directory for compilation + original_cwd = os.getcwd() + os.chdir(safe_temp_dir) + + try: + print(f'[report_customization_api] CustomizeReport:compiling in {os.getcwd()}') + print(f'[report_customization_api] CustomizeReport:tex file exists: {os.path.exists("filtered_report.tex")}') + + # Compile LaTeX - use system pdflatex (same as working design_report_csv_view.py) + print(f'[report_customization_api] CustomizeReport:using system pdflatex') + + # Check if pdflatex is available + try: + subprocess.run(['pdflatex', '--version'], capture_output=True, text=True, timeout=10) + print('[report_customization_api] CustomizeReport:pdflatex is available') + except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError): + print('[report_customization_api] CustomizeReport:pdflatex not found, trying alternative approach') + return Response( + {"error": "LaTeX (pdflatex) not found. Please install a LaTeX distribution like MiKTeX or TeX Live."}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + + try: + if platform.system().lower() == 'windows': + print('[report_customization_api] CustomizeReport:running pdflatex on windows') + result = subprocess.run([ + 'pdflatex', '-interaction=nonstopmode', + 'filtered_report.tex' + ], capture_output=True, text=True, timeout=60) + else: + print('[report_customization_api] CustomizeReport:running pdflatex on unix') + result = subprocess.run([ + 'pdflatex', '-interaction=nonstopmode', + 'filtered_report.tex' + ], capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired as e: + print(f'[report_customization_api] CustomizeReport:pdflatex timeout: {e}') + raise + except subprocess.CalledProcessError as e: + print(f'[report_customization_api] CustomizeReport:pdflatex process error: {e}') + raise + except Exception as e: + print(f'[report_customization_api] CustomizeReport:pdflatex general error: {e}') + raise + + print(f'[report_customization_api] CustomizeReport:pdflatex result: {result.returncode}') + print(f'[report_customization_api] CustomizeReport:stdout: {result.stdout[:200]}') + print(f'[report_customization_api] CustomizeReport:stderr: {result.stderr[:200]}') + + # Check if PDF was generated successfully + # if result.returncode == 0 and os.path.exists(pdf_path): + if os.path.exists(pdf_path): + print('[report_customization_api] CustomizeReport:pdflatex:success', { 'report_id': report_id, 'pdf_path': pdf_path }) + # Return PDF file + response = FileResponse( + open(pdf_path, 'rb'), + content_type='application/pdf', + filename=f'osdag_custom_report_{report_id}.pdf' + ) + return response + else: + print('[report_customization_api] CustomizeReport:pdflatex:failure', { 'code': result.returncode }) + error_msg = "PDF compilation failed" + if result.stderr: + error_msg += f"\n\nErrors:\n{result.stderr[:500]}" + if result.stdout: + error_msg += f"\n\nOutput:\n{result.stdout[:500]}" + + return Response( + {"error": error_msg}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + + except subprocess.TimeoutExpired: + return Response( + {"error": "PDF compilation timed out (>60s)"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + except FileNotFoundError: + return Response( + {"error": "LaTeX (pdflatex) not found. Please install a LaTeX distribution."}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + finally: + # Restore original working directory + os.chdir(original_cwd) + + except Exception as e: + return Response( + {"error": f"Failed to customize report: {str(e)}"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + + +def generate_initial_report_core(mapped_data): + """ + Shared core for initial report generation. + + Responsibilities: + - Call CreateDesignReport with the provided mapped_data + - Read the generated LaTeX file + - Parse sections using LaTeXParser + - Return a (payload, status_code) tuple suitable for DRF Response + """ + try: + print(f"[GenerateInitialReportCore] mapped_data keys: {list(mapped_data.keys())}") + from .design_report_csv_view import CreateDesignReport + + class MockRequest: + def __init__(self, data): + self.data = data + + # Run the existing CreateDesignReport logic + print("[GenerateInitialReportCore] Creating CreateDesignReport instance...") + create_report = CreateDesignReport() + mock_request = MockRequest(mapped_data) + + print("[GenerateInitialReportCore] Calling CreateDesignReport.post()...") + response = create_report.post(mock_request) + print("[GenerateInitialReportCore] CreateDesignReport response received") + status_code = getattr(response, "status_code", None) + print(f"[GenerateInitialReportCore] status_code: {status_code}") + + if status_code != status.HTTP_201_CREATED: + # Bubble up detailed error info when available + error_payload = {} + if hasattr(response, "data"): + error_payload = response.data + print(f"[GenerateInitialReportCore] error response.data: {error_payload}") + else: + error_payload = {"error": "Failed to generate design report"} + + # Prefer any existing message/error keys for client display + message = None + if isinstance(error_payload, dict): + message = ( + error_payload.get("error") + or error_payload.get("message") + or str(error_payload) + ) + else: + message = str(error_payload) + + return ( + { + "success": False, + "error": message or "Failed to generate design report", + "details": error_payload if isinstance(error_payload, dict) else None, + }, + status.HTTP_400_BAD_REQUEST, + ) + + # On success, extract report_id from response + response_data = getattr(response, "data", {}) or {} + print( + "[GenerateInitialReportCore] success response.data keys:", + list(response_data.keys()) if isinstance(response_data, dict) else "N/A", + ) + report_id = response_data.get("report_id") + print(f"[GenerateInitialReportCore] extracted report_id: {report_id}") + + if not report_id: + return ( + { + "success": False, + "error": "report_id missing from design report response", + }, + status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + + # Locate and read the generated LaTeX file. + # New layout (per-report folder): + # file_storage/design_report/{report_id}/{report_id}.tex + # Legacy layout (flat): + # file_storage/design_report/{report_id}.tex + base_dir = os.path.join(os.getcwd(), "file_storage", "design_report") + new_style_path = os.path.join(base_dir, report_id, f"{report_id}.tex") + legacy_path = os.path.join(base_dir, f"{report_id}.tex") + + if os.path.exists(new_style_path): + tex_file_path = new_style_path + else: + tex_file_path = legacy_path + + print(f"[GenerateInitialReportCore] LaTeX file path: {tex_file_path}") + exists = os.path.exists(tex_file_path) + print(f"[GenerateInitialReportCore] LaTeX file exists: {exists}") + + if not exists: + return ( + { + "success": False, + "error": f"LaTeX file not found at {tex_file_path}", + "report_id": report_id, + }, + status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + + file_size = os.path.getsize(tex_file_path) + print(f"[GenerateInitialReportCore] LaTeX file size: {file_size} bytes") + + with open(tex_file_path, "r", encoding="utf-8") as f: + latex_content = f.read() + print( + "[GenerateInitialReportCore] LaTeX content length:", + len(latex_content), + ) + + # Parse sections using the existing LaTeXParser + parser = LaTeXParser() + sections = parser.parse_sections(latex_content) + print( + "[GenerateInitialReportCore] Parsed sections count:", + len(sections), + ) + print( + "[GenerateInitialReportCore] Section keys:", + list(sections.keys()), + ) + + payload = { + "success": True, + "report_id": report_id, + "sections": sections, + "message": "LaTeX report generated successfully", + } + return payload, status.HTTP_201_CREATED + + except Exception as e: + print("[GenerateInitialReportCore] Exception:", str(e)) + import traceback + + traceback.print_exc() + return ( + { + "success": False, + "error": f"Failed to generate initial report: {str(e)}", + }, + status.HTTP_500_INTERNAL_SERVER_ERROR, + ) + + diff --git a/backend/apps/core/api/design/tests/__init__.py b/backend/apps/core/api/design/tests/__init__.py new file mode 100644 index 000000000..d6cf9eb81 --- /dev/null +++ b/backend/apps/core/api/design/tests/__init__.py @@ -0,0 +1,3 @@ +# Tests for design API + + diff --git a/backend/apps/core/api/design/tests/test_design_report_with_images.py b/backend/apps/core/api/design/tests/test_design_report_with_images.py new file mode 100644 index 000000000..86427ab87 --- /dev/null +++ b/backend/apps/core/api/design/tests/test_design_report_with_images.py @@ -0,0 +1,84 @@ +""" +Integration tests for design report generation with images. +""" + +import os +import tempfile +from django.test import TestCase +from rest_framework.test import APIClient + + +class TestDesignReportWithImages(TestCase): + """Smoke tests for report generation endpoints (slug + legacy)""" + + def setUp(self): + self.client = APIClient() + self.temp_dir = tempfile.mkdtemp() + + def tearDown(self): + import shutil + + shutil.rmtree(self.temp_dir, ignore_errors=True) + + def _dummy_metadata(self): + return { + "ProfileSummary": { + "CompanyName": "TestCo", + "CompanyLogo": "", + "Group/TeamName": "TestTeam", + "Designer": "Tester", + }, + "ProjectTitle": "Test Project", + "Subtitle": "", + "JobNumber": "1", + "AdditionalComments": "No comments", + "Client": "Test Client", + } + + def _dummy_input_values(self): + # Minimal placeholder payload; actual fields are validated + # by the underlying desktop module. These tests only assert + # that the endpoints are wired and return a JSON response. + return {"dummy": True} + + def test_legacy_generate_initial_endpoint_available(self): + """ + Ensure the legacy /api/report/generate-initial/ endpoint is wired. + + We don't assert 201 here because full desktop modules are not + available in this lightweight test environment. + """ + payload = { + "metadata": self._dummy_metadata(), + "module_id": "FinPlateConnection", + "input_values": self._dummy_input_values(), + "design_status": True, + "logs": [], + } + response = self.client.post( + "/api/report/generate-initial/", data=payload, format="json" + ) + # Endpoint should exist and return JSON; status code may vary (e.g. 400) + assert response.status_code in (200, 400, 500) + assert isinstance(response.data, dict) + + def test_slug_generate_initial_endpoint_shear_exists(self): + """ + Ensure the slug-based shear-connection report endpoint is wired. + """ + payload = { + "metadata": self._dummy_metadata(), + "input_values": self._dummy_input_values(), + "design_status": True, + "logs": [], + } + response = self.client.post( + "/api/modules/shear-connection/fin-plate/report/generate-initial/", + data=payload, + format="json", + ) + # Endpoint should exist; body should be JSON/dict + assert response.status_code in (200, 400, 500) + assert isinstance(response.data, dict) + + diff --git a/backend/apps/core/api/modules/__init__.py b/backend/apps/core/api/modules/__init__.py new file mode 100644 index 000000000..3e65ec34f --- /dev/null +++ b/backend/apps/core/api/modules/__init__.py @@ -0,0 +1,9 @@ +""" +Module Discovery APIs +""" +from .modules_api import GetModules + +__all__ = [ + 'GetModules', +] + diff --git a/backend/apps/core/api/modules/modules_api.py b/backend/apps/core/api/modules/modules_api.py new file mode 100644 index 000000000..71461eced --- /dev/null +++ b/backend/apps/core/api/modules/modules_api.py @@ -0,0 +1,34 @@ +""" + This file includes the Modules API + Get Module Data. + Get Modules API (class GetModules(View)): + Accepts GET requests. + Returns all developed modules in json format. +""" +from django.http import HttpResponse, HttpRequest +from django.views import View +from django.views.decorators.csrf import csrf_exempt +from django.utils.decorators import method_decorator +# Use the new `apps.core.module_finder` which exposes `module_dict` and +# `developed_modules`. The module_finder implementation already falls back +# to the legacy `osdag_api` where necessary, so import that single source. +from apps.core.module_finder import module_dict, developed_modules +import json +import typing + +# Author: Aaranyak Ghosh + +@method_decorator(csrf_exempt, name='dispatch') +class GetModules(View): + """ + Get Module Data. + Get Modules API (class GetModules(View)): + Accepts GET requests. + Returns all developed modules in json format. + """ + def get(self,request: HttpRequest) -> HttpResponse: + module_data = json.dumps(module_dict) # Convert module data to json. + response = HttpResponse(status=200) # Status code 200 - success. + response["content-type"] = "application/json" # Set content-type header to json. + response.write(module_data) # Write module data to http response body. + return response diff --git a/backend/apps/core/api/projects/__init__.py b/backend/apps/core/api/projects/__init__.py new file mode 100644 index 000000000..28229e8b9 --- /dev/null +++ b/backend/apps/core/api/projects/__init__.py @@ -0,0 +1,11 @@ +""" +Project Management APIs +""" +from .project_api import ProjectAPI, ProjectDetailAPI, ProjectByNameAPI +from .osi_api import SaveOsiFromInputs, OpenOsiUpload, OpenOsiById, ModuleRoutes, ProjectOsiDownload + +__all__ = [ + 'ProjectAPI', 'ProjectDetailAPI', 'ProjectByNameAPI', + 'SaveOsiFromInputs', 'OpenOsiUpload', 'OpenOsiById', 'ModuleRoutes', 'ProjectOsiDownload', +] + diff --git a/backend/apps/core/api/projects/osi_api.py b/backend/apps/core/api/projects/osi_api.py new file mode 100644 index 000000000..e91ca8995 --- /dev/null +++ b/backend/apps/core/api/projects/osi_api.py @@ -0,0 +1,203 @@ +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework.parsers import MultiPartParser, FormParser +from django.views.decorators.csrf import csrf_exempt +from django.utils.decorators import method_decorator +from django.http import JsonResponse +from django.core.files.base import ContentFile + +from apps.core.models import OsiFile +from apps.core.serializers import OsiFileSerializer +from apps.core.utils.osi_files import build_osi_payload, make_osifile_contentfile, parse_osi +from apps.core.models import Project + + +@method_decorator(csrf_exempt, name='dispatch') +class SaveOsiFromInputs(APIView): + permission_classes = [AllowAny] # Allow guests to generate OSI for download + + def post(self, request): + try: + name = request.data.get('name') + module_id = request.data.get('module_id') + inputs = request.data.get('inputs') + inline = False + try: + # allow caller to force inline/base64 response even for authenticated users + inline = bool(request.data.get('inline')) or bool(request.GET.get('inline')) + except Exception: + inline = False + + payload = build_osi_payload(name=name, module_id=module_id, inputs=inputs or {}) + + # Check if user is guest (guests don't send authentication tokens) + is_guest = not (hasattr(request, 'user') and request.user.is_authenticated) + + # For guests OR when inline flag is set: return OSI content for download (no DB save) + if is_guest or inline: + import base64 + content_file = make_osifile_contentfile(payload) + content_bytes = content_file.read() + content_base64 = base64.b64encode(content_bytes).decode('ascii') + safe_name = (name or 'project').replace(' ', '_')[:50] + filename = f"{safe_name}_{module_id}.osi" + + return JsonResponse({ + 'success': True, + 'is_guest': True, + 'filename': filename, + 'content_base64': content_base64, + 'message': 'OSI file generated. Download available.' + }, safe=False, status=200) + + # For authenticated users: save to database + # Check email verification status (set by FirebaseAuthentication middleware) + email_verified = getattr(request, 'email_verified', False) + if not email_verified: + return JsonResponse({ + 'success': False, + 'error': 'Please verify your email to save projects. Check your inbox for the verification link.' + }, safe=False, status=403) + + content_file = make_osifile_contentfile(payload) + + # Determine owner email from JWT or user + owner_email = getattr(request.user, 'email', None) + if not owner_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + owner_email = request.auth.get('email') + + osifile = OsiFile.objects.create(owner_email=owner_email, original_name=f"{name}.osi") + # generate a safe filename + safe_name = (name or 'project').replace(' ', '_')[:50] + filename = f"{safe_name}_{module_id}.osi" + osifile.file.save(filename, content_file, save=True) + try: + osifile.size_bytes = osifile.file.size + except Exception: + pass + osifile.content_type = 'text/plain' + osifile.save() + + serializer = OsiFileSerializer(osifile) + # Do not create a Project here. Frontend will update existing project with this URL. + return JsonResponse({ + 'success': True, + 'is_guest': False, + 'data': serializer.data, + 'url': osifile.file.url + }, safe=False, status=201) + except Exception as e: + print('Error in SaveOsiFromInputs:', e) + return JsonResponse({'success': False, 'error': str(e)}, safe=False, status=400) + + +@method_decorator(csrf_exempt, name='dispatch') +class OpenOsiUpload(APIView): + # Allow OSI load without auth so inputs can be populated directly + permission_classes = [AllowAny] + parser_classes = (MultiPartParser, FormParser) + + def post(self, request): + try: + # Optional policy: guests may open uploads read-only; configurable. For now allow. + uploaded = request.FILES.get('file') + if not uploaded: + return JsonResponse({'success': False, 'error': 'No file provided under field "file"'}, safe=False, status=400) + + content = uploaded.read().decode('utf-8', errors='replace') + module_id, name, inputs = parse_osi(content) + # Map to new response contract as well + mapped = { + 'success': True, + 'module_id': module_id, + 'name': name, + 'inputs': inputs, + 'module': None, + 'submodule': module_id, + 'inputs_json': inputs, + } + return JsonResponse(mapped, safe=False) + except Exception as e: + print('Error in OpenOsiUpload:', e) + return JsonResponse({'success': False, 'error': str(e)}, safe=False, status=400) + + +@method_decorator(csrf_exempt, name='dispatch') +class OpenOsiById(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request, osifile_id): + try: + osifile = OsiFile.objects.get(id=osifile_id) + # permission: only owner can read if owner_email set + owner_email = osifile.owner_email + requester_email = getattr(request.user, 'email', None) + if not requester_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + requester_email = request.auth.get('email') + if owner_email and requester_email != owner_email: + return JsonResponse({'success': False, 'error': 'Access denied'}, safe=False, status=403) + content = osifile.file.read().decode('utf-8', errors='replace') + module_id, name, inputs = parse_osi(content) + return JsonResponse({'success': True, 'module_id': module_id, 'name': name, 'inputs': inputs, 'module': None, 'submodule': module_id, 'inputs_json': inputs, 'url': osifile.file.url}, safe=False) + except OsiFile.DoesNotExist: + return JsonResponse({'success': False, 'error': 'OSI file not found'}, safe=False, status=404) + except Exception as e: + print('Error in OpenOsiById:', e) + return JsonResponse({'success': False, 'error': str(e)}, safe=False, status=400) + + +@method_decorator(csrf_exempt, name='dispatch') +class ModuleRoutes(APIView): + def get(self, request): + # central mapping (backend source of truth) used by frontend to route modules + routes = { + 'fp': '/design/connections/shear/fin_plate', + 'ca': '/design/connections/shear/cleat_angle', + 'ep': '/design/connections/shear/end_plate', + 'sa': '/design/connections/shear/seatAngle', + 'cpb': '/design/connections/beam-to-beam-splice/cover_plate_bolted', + 'cpw': '/design/connections/beam-to-beam-splice/cover_plate_welded', + 'boltedtoendplate': '/design/tension-member/bolted_to_end_gusset', + 'ssb': '/design/FlexureMember/simply_supported_beam', + } + return JsonResponse({'success': True, 'routes': routes}, safe=False) + + +@method_decorator(csrf_exempt, name='dispatch') +class ProjectOsiDownload(APIView): + permission_classes = [IsAuthenticated] + + def get(self, request, project_id): + try: + # Determine requester email + requester_email = getattr(request.user, 'email', None) + if not requester_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + requester_email = request.auth.get('email') + + project = Project.objects.get(id=project_id, user_email=requester_email) + + inputs = getattr(project, 'inputs_json', None) or {} + if inputs is None: + inputs = {} + + # Choose module identifier for OSI payload (prefer submodule, fallback to module) + module_id = getattr(project, 'submodule', None) or getattr(project, 'module', None) or 'FinPlateConnection' + + payload = build_osi_payload(name=project.name or 'project', module_id=module_id, inputs=inputs) + content_file = make_osifile_contentfile(payload) + + safe_name = (project.name or 'project').replace(' ', '_')[:50] + filename = f"{safe_name}.osi" + + from django.http import HttpResponse + resp = HttpResponse(content_file.read(), content_type='text/plain') + resp['Content-Disposition'] = f'attachment; filename="{filename}"' + return resp + except Project.DoesNotExist: + return JsonResponse({'success': False, 'error': 'Project not found or access denied'}, safe=False, status=404) + except Exception as e: + print('Error generating OSI for project:', e) + return JsonResponse({'success': False, 'error': str(e)}, safe=False, status=400) + diff --git a/backend/apps/core/api/projects/project_api.py b/backend/apps/core/api/projects/project_api.py new file mode 100644 index 000000000..2976f5367 --- /dev/null +++ b/backend/apps/core/api/projects/project_api.py @@ -0,0 +1,341 @@ +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from rest_framework.permissions import IsAuthenticated +from django.views.decorators.csrf import csrf_exempt +from django.utils.decorators import method_decorator +from django.http import JsonResponse +from apps.core.models import Project +from apps.core.permissions import IsEmailVerified +import json + +@method_decorator(csrf_exempt, name='dispatch') +class ProjectAPI(APIView): + """API for managing user projects""" + permission_classes = [IsAuthenticated] + + def get(self, request): + """Get user-specific projects (for recent projects list)""" + try: + # if not request.user or not request.user.is_authenticated: + # return JsonResponse({'success': False, 'error': 'Authentication required'}, safe=False, status=401) + + # Disallow guest users from listing projects (guests don't send authentication tokens) + if not (hasattr(request, 'user') and request.user.is_authenticated): + return JsonResponse({'success': False, 'error': 'Guest users cannot access projects'}, safe=False, status=403) + + user_email = getattr(request.user, 'email', None) + if not user_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + user_email = request.auth.get('email') + # Filter projects by user + projects = Project.objects.filter(user_email=user_email).order_by('-updated_at')[:10] + print(f"Found {projects.count()} projects for user {user_email}") + + project_list = [] + for project in projects: + project_list.append({ + 'id': project.id, + 'name': project.name, + 'module': getattr(project, 'module', None), + 'submodule': getattr(project, 'submodule', None), + # keep legacy field for existing frontend behavior + 'module_id': getattr(project, 'submodule', None), + 'osi_file_path': project.osi_file_path, + 'created_at': project.created_at.isoformat(), + 'updated_at': project.updated_at.isoformat(), + }) + + print(f"Returning {len(project_list)} projects") + return JsonResponse({ + 'success': True, + 'projects': project_list + }, safe=False) + + except Exception as e: + print(f"Error getting projects: {e}") + return JsonResponse({ + 'success': False, + 'error': str(e) + }, safe=False, status=400) + + def post(self, request): + """Create a new project""" + try: + # Check email verification status (set by FirebaseAuthentication middleware) + email_verified = getattr(request, 'email_verified', False) + if not email_verified: + return JsonResponse({ + 'success': False, + 'error': 'Please verify your email to create projects. Check your inbox for the verification link.' + }, safe=False, status=403) + + # Disallow guest users from creating projects (guests don't send authentication tokens) + if not (hasattr(request, 'user') and request.user.is_authenticated): + return JsonResponse({'success': False, 'error': 'Guest users cannot create projects'}, safe=False, status=403) + + data = request.data + print(f"Creating project with data: {data}") + + # Validate required fields + required_fields = ['name'] + for field in required_fields: + if field not in data: + return JsonResponse({ + 'success': False, + 'error': f'Missing required field: {field}' + }, safe=False, status=400) + + # Determine user email from authenticated user or JWT claims + user_email = getattr(request.user, 'email', None) + if not user_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + user_email = request.auth.get('email') + if not user_email: + return JsonResponse({'success': False, 'error': 'Authenticated user email not found'}, safe=False, status=400) + + # Create new project with user email + project = Project.objects.create( + name=data['name'], + module=data.get('module'), + submodule=data.get('submodule') or data.get('module_id'), + inputs_json=data.get('inputs_json'), + osi_file_path=data.get('osi_file_path'), + user_email=user_email + ) + + return JsonResponse({ + 'success': True, + 'project_id': project.id, + 'message': 'Project created successfully' + }, safe=False, status=201) + + except Exception as e: + print(f"Error creating project: {e}") + return JsonResponse({ + 'success': False, + 'error': str(e) + }, safe=False, status=400) + +@method_decorator(csrf_exempt, name='dispatch') +class ProjectDetailAPI(APIView): + """API for individual project operations""" + permission_classes = [IsAuthenticated] + + def get(self, request, project_id): + """Get a specific project""" + try: + # if not request.user or not request.user.is_authenticated: + # return JsonResponse({'success': False, 'error': 'Authentication required'}, safe=False, status=401) + # Disallow guest users from reading projects (guests don't send authentication tokens) + if not (hasattr(request, 'user') and request.user.is_authenticated): + return JsonResponse({'success': False, 'error': 'Guest users cannot access projects'}, safe=False, status=403) + user_email = getattr(request.user, 'email', None) + if not user_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + user_email = request.auth.get('email') + + # Get project and verify ownership + project = Project.objects.get(id=project_id, user_email=user_email) + print(f"Found project: {project.name}") + + return JsonResponse({ + 'success': True, + 'project': { + 'id': project.id, + 'name': project.name, + 'module': getattr(project, 'module', None), + 'submodule': getattr(project, 'submodule', None), + 'module_id': getattr(project, 'submodule', None), + 'inputs_json': getattr(project, 'inputs_json', None), + 'osi_file_path': project.osi_file_path, + 'created_at': project.created_at.isoformat(), + 'updated_at': project.updated_at.isoformat() + } + }, safe=False) + + except Project.DoesNotExist: + print(f"Project {project_id} not found or access denied") + return JsonResponse({ + 'success': False, + 'error': 'Project not found or access denied' + }, safe=False, status=404) + except Exception as e: + print(f"Error getting project {project_id}: {e}") + return JsonResponse({ + 'success': False, + 'error': str(e) + }, safe=False, status=400) + + def put(self, request, project_id): + """Update project data (name, inputs_json, osi_file_path)""" + try: + # Check email verification status (set by FirebaseAuthentication middleware) + email_verified = getattr(request, 'email_verified', False) + if not email_verified: + return JsonResponse({ + 'success': False, + 'error': 'Please verify your email to save projects. Check your inbox for the verification link.' + }, safe=False, status=403) + + # Disallow guest users from updating projects (guests don't send authentication tokens) + if not (hasattr(request, 'user') and request.user.is_authenticated): + return JsonResponse({'success': False, 'error': 'Guest users cannot update projects'}, safe=False, status=403) + user_email = getattr(request.user, 'email', None) + if not user_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + user_email = request.auth.get('email') + + print(f"Project API PUT - Project ID: {project_id}, User email: {user_email}") + print(f"Request data: {request.data}") + + # Get project and verify ownership + project = Project.objects.get(id=project_id, user_email=user_email) + data = request.data + + print(f"Found project: {project.name}") + print(f"Updating with data: {data}") + + # Update fields if provided + if 'name' in data: + project.name = data['name'] + if 'module' in data: + project.module = data['module'] + if 'submodule' in data or 'module_id' in data: + project.submodule = data.get('submodule') or data.get('module_id') + if 'inputs_json' in data: + project.inputs_json = data['inputs_json'] + if 'osi_file_path' in data: + project.osi_file_path = data['osi_file_path'] + + project.save() + print(f"Project {project_id} updated successfully") + + return JsonResponse({ + 'success': True, + 'message': 'Project updated successfully' + }, safe=False) + + except Project.DoesNotExist: + print(f"Project {project_id} not found or access denied") + return JsonResponse({ + 'success': False, + 'error': 'Project not found or access denied' + }, safe=False, status=404) + except Exception as e: + print(f"Error updating project {project_id}: {e}") + return JsonResponse({ + 'success': False, + 'error': str(e) + }, safe=False, status=400) + + def delete(self, request, project_id): + """Delete a project""" + try: + # if not request.user or not request.user.is_authenticated: + # return JsonResponse({'success': False, 'error': 'Authentication required'}, safe=False, status=401) + # Disallow guest users from deleting projects (guests don't send authentication tokens) + if not (hasattr(request, 'user') and request.user.is_authenticated): + return JsonResponse({'success': False, 'error': 'Guest users cannot delete projects'}, safe=False, status=403) + user_email = getattr(request.user, 'email', None) + if not user_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + user_email = request.auth.get('email') + + # Get project and verify ownership + project = Project.objects.get(id=project_id, user_email=user_email) + project.delete() + + return JsonResponse({ + 'success': True, + 'message': 'Project deleted successfully' + }, safe=False) + + except Project.DoesNotExist: + return JsonResponse({ + 'success': False, + 'error': 'Project not found or access denied' + }, safe=False, status=404) + except Exception as e: + print(f"Error deleting project {project_id}: {e}") + return JsonResponse({ + 'success': False, + 'error': str(e) + }, safe=False, status=400) + +@method_decorator(csrf_exempt, name='dispatch') +class ProjectByNameAPI(APIView): + """API for finding and updating projects by name""" + permission_classes = [IsAuthenticated] + + def get(self, request, project_name): + """Get a specific project by name""" + try: + # if not request.user or not request.user.is_authenticated: + # return JsonResponse({'success': False, 'error': 'Authentication required'}, safe=False, status=401) + # Disallow guest users from reading projects by name (guests don't send authentication tokens) + if not (hasattr(request, 'user') and request.user.is_authenticated): + return JsonResponse({'success': False, 'error': 'Guest users cannot access projects'}, safe=False, status=403) + user_email = getattr(request.user, 'email', None) + if not user_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + user_email = request.auth.get('email') + + # Get project by name and verify ownership + project = Project.objects.get(name=project_name, user_email=user_email) + print(f"Found project: {project.name}") + + return JsonResponse({ + 'success': True, + 'data': { + 'id': project.id, + 'name': project.name, + 'osi_file_path': project.osi_file_path, + 'created_at': project.created_at.isoformat(), + 'updated_at': project.updated_at.isoformat() + } + }, safe=False) + + except Project.DoesNotExist: + print(f"Project '{project_name}' not found or access denied") + return JsonResponse({ + 'success': False, + 'error': 'Project not found or access denied' + }, safe=False, status=404) + except Exception as e: + print(f"Error getting project '{project_name}': {e}") + return JsonResponse({ + 'success': False, + 'error': str(e) + }, safe=False, status=400) + + def put(self, request, project_name): + """Update project data (name, osi_file_path) by name""" + try: + # if not request.user or not request.user.is_authenticated: + # return JsonResponse({'success': False, 'error': 'Authentication required'}, safe=False, status=401) + # Disallow guest users from updating projects by name (guests don't send authentication tokens) + if not (hasattr(request, 'user') and request.user.is_authenticated): + return JsonResponse({'success': False, 'error': 'Guest users cannot update projects'}, safe=False, status=403) + user_email = getattr(request.user, 'email', None) + if not user_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + user_email = request.auth.get('email') + + # Get project and verify ownership by name + project = Project.objects.get(name=project_name, user_email=user_email) + data = request.data + + # Update fields if provided + if 'name' in data: + project.name = data['name'] + if 'osi_file_path' in data: + project.osi_file_path = data['osi_file_path'] + project.save() + return JsonResponse({ + 'success': True, + 'message': 'Project updated successfully' + }, safe=False) + except Project.DoesNotExist: + return JsonResponse({ + 'success': False, + 'error': 'Project not found or access denied' + }, safe=False, status=404) + except Exception as e: + return JsonResponse({ + 'success': False, + 'error': str(e) + }, safe=False, status=400) diff --git a/backend/apps/core/apps.py b/backend/apps/core/apps.py new file mode 100644 index 000000000..24e2c2767 --- /dev/null +++ b/backend/apps/core/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class OsdagConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.core' diff --git a/backend/apps/core/data/design_types.py b/backend/apps/core/data/design_types.py new file mode 100644 index 000000000..a9f647d9e --- /dev/null +++ b/backend/apps/core/data/design_types.py @@ -0,0 +1,192 @@ +design_type_data = { + 'data': [ + { + 'id': 1, + 'name': 'connections' + }, + { + 'id': 2, + 'name': 'Tension_Member' + }, + { + 'id': 3, + 'name': 'Compression_Member' + }, + { + 'id': 4, + 'name': 'Flexural_Member' + }, + { + 'id': 5, + 'name': 'Beam_Column' + }, + { + 'id': 6, + 'name': 'Plate_Girder' + }, + { + 'id': 7, + 'name': 'Truss' + }, + { + 'id': 8, + 'name': '2D_Frame' + }, + { + 'id': 9, + 'name': '3D_Frame' + }, + { + 'id': 10, + 'name': 'Group_Design' + } + ], + 'success': True, +} + +connections_data = { + 'data': [ + { + 'id': 1, + 'name': 'Shear_Connection' + }, + { + 'id': 2, + 'name': 'Moment_Connection' + }, + { + 'id': 3, + 'name': 'Base_Plate' + }, + { + 'id': 4, + 'name': 'Truss_Connection' + }], + 'has_subtypes': True, + 'success': True +} + +shear_connection = { + 'data': [ + { + 'id': 1, + 'name': 'Fin_Plate', + 'image_name': 'sc_fin_plate' + }, + { + 'id': 2, + 'name': 'Cleat_Angle', + 'image_name': 'sc_cleat_angle' + }, + { + 'id': 3, + 'name': 'End_Plate', + 'image_name': 'sc_end_plate' + }, + { + 'id': 4, + 'name': 'Seated_Angle', + 'image_name': 'sc_seated_angle' + }], + 'has_subtypes': False, + 'success': True +} + +moment_connection = { + 'data': [ + { + 'id': 1, + 'name': 'Beam-To-Beam_Splice' + }, + { + 'id': 2, + 'name': 'Beam-To-Column' + }, + { + 'id': 3, + 'name': 'Column-To-Column_Splice' + }, + { + 'id': 4, + 'name': 'PEB' + }], + 'has_subtypes': True, + 'success': True +} + +b2b_splice = { + 'data': [{ + 'id': 1, + 'name': 'Cover_Plate_Bolted', + 'image_name': 'mc_btb_cpb' + }, + { + 'id': 2, + 'name': 'Cover_Plate_Welded', + 'image_name': 'mc_btb_cpw' + }, + { + 'id': 3, + 'name': 'End_Plate', + 'image_name': 'mc_btb_ep' + }], + 'has_subtypes': False, + 'success': True +} + +b2column = { + 'data': [{ + 'id': 1, + 'name': 'End_Plate', + 'image_name': 'mc_btc_ep' + }], + 'has_subtypes': False, + 'success': True +} + +c2c_splice = { + 'data': [ + { + 'id': 1, + 'name': 'Cover_Plate_Bolted', + 'image_name': 'mc_ctc_cpb' + }, + { + 'id': 2, + 'name': 'Cover_Plate_Welded', + 'image_name': 'mc_ctc_cpw' + }, + { + 'id': 3, + 'name': 'End_Plate', + 'image_name': 'mc_ctc_ep' + }], + 'has_subtypes': False, + 'success': True +} + +base_plate = { + 'data': [{ + 'id': 1, + 'name': 'Base_Plate_Connection', + 'image_name': 'base_plate' + }], + 'has_subtypes': False, + 'success': True +} + +tension_member = { + 'data': [ + { + 'id': 1, + 'name': 'Bolted_To_End_Gusset', + 'image_name': 'bolted_to_end' + }, + { + 'id': 2, + 'name': 'Welded_To_End_Gusset', + 'image_name': 'welded_to_end' + }], + 'has_subtypes': False, + 'success': True +} diff --git a/backend/apps/core/middleware/__init__.py b/backend/apps/core/middleware/__init__.py new file mode 100644 index 000000000..119f268a2 --- /dev/null +++ b/backend/apps/core/middleware/__init__.py @@ -0,0 +1,2 @@ +# Middleware package + diff --git a/backend/apps/core/middleware/firebase_auth.py b/backend/apps/core/middleware/firebase_auth.py new file mode 100644 index 000000000..83e681424 --- /dev/null +++ b/backend/apps/core/middleware/firebase_auth.py @@ -0,0 +1,96 @@ +""" +Firebase Authentication Middleware for Django REST Framework +Verifies Firebase tokens and syncs users to Django User model +""" + +from firebase_admin import auth as firebase_auth +from django.contrib.auth.models import User +from rest_framework.authentication import BaseAuthentication +from rest_framework.exceptions import AuthenticationFailed + + +class FirebaseAuthentication(BaseAuthentication): + """ + Custom authentication class for Firebase tokens. + Verifies Firebase ID tokens and syncs users to Django User model. + """ + + def authenticate(self, request): + """ + Authenticate request using Firebase token. + + Returns: + tuple: (user, None) if authenticated, None otherwise + """ + auth_header = request.META.get('HTTP_AUTHORIZATION', '') + + if not auth_header.startswith('Bearer '): + return None + + token = auth_header.split('Bearer ')[1] + + if not token: + return None + + try: + # Verify Firebase token + decoded = firebase_auth.verify_id_token(token) + uid = decoded.get('uid') + email = decoded.get('email') + email_verified = decoded.get('email_verified', False) + + if not uid: + raise AuthenticationFailed('Invalid token: missing UID') + + # Sync to Django User (use Firebase UID as username) + user, created = User.objects.get_or_create( + username=uid, + defaults={ + 'email': email or '', + } + ) + + # Update email if changed + if email and user.email != email: + user.email = email + user.save() + + # Sync UserAccount if it doesn't exist (for backward compatibility) + from apps.core.models import UserAccount + user_account, _ = UserAccount.objects.get_or_create( + username=uid, + defaults={ + 'user': user, + 'email': email or '', + 'allInputValueFiles': [] + } + ) + + # Update UserAccount if user link is missing or email changed + needs_save = False + if not user_account.user: + user_account.user = user + needs_save = True + if email and user_account.email != email: + user_account.email = email + needs_save = True + if user_account.user != user: + user_account.user = user + needs_save = True + + if needs_save: + user_account.save() + + # Attach Firebase metadata to request for use in views + request.firebase_uid = uid + request.email_verified = email_verified + + return (user, None) + + except firebase_auth.InvalidIdTokenError: + raise AuthenticationFailed('Invalid Firebase token') + except firebase_auth.ExpiredIdTokenError: + raise AuthenticationFailed('Firebase token has expired') + except Exception as e: + raise AuthenticationFailed(f'Authentication failed: {str(e)}') + diff --git a/backend/apps/core/migrations/0001_initial.py b/backend/apps/core/migrations/0001_initial.py new file mode 100644 index 000000000..e1d919e9a --- /dev/null +++ b/backend/apps/core/migrations/0001_initial.py @@ -0,0 +1,391 @@ +# Generated by Django 4.2.21 on 2025-06-19 15:14 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Anchor_Bolt', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Diameter', models.TextField()), + ], + options={ + 'db_table': 'Anchor_Bolt', + }, + ), + migrations.CreateModel( + name='Angle_Pitch', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Nominal_Leg', models.IntegerField()), + ('Max_Bolt_Dia', models.IntegerField()), + ('Bolt_lines', models.IntegerField()), + ('S1', models.IntegerField(null=True)), + ('S2', models.IntegerField(null=True)), + ('S3', models.IntegerField(null=True)), + ], + options={ + 'db_table': 'Angle_Pitch', + }, + ), + migrations.CreateModel( + name='Angles', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Designation', models.CharField(max_length=50)), + ('Mass', models.DecimalField(decimal_places=2, max_digits=10)), + ('Area', models.DecimalField(decimal_places=2, max_digits=10)), + ('a', models.DecimalField(decimal_places=2, max_digits=10)), + ('b', models.DecimalField(decimal_places=2, max_digits=10)), + ('t', models.DecimalField(decimal_places=2, max_digits=10)), + ('R1', models.DecimalField(decimal_places=2, max_digits=10)), + ('R2', models.DecimalField(decimal_places=2, max_digits=10)), + ('Cz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Cy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Alpha', models.DecimalField(decimal_places=2, max_digits=10)), + ('lumax', models.DecimalField(decimal_places=2, max_digits=10)), + ('lvmin', models.DecimalField(decimal_places=2, max_digits=10)), + ('rz', models.DecimalField(decimal_places=2, max_digits=10)), + ('ry', models.DecimalField(decimal_places=2, max_digits=10)), + ('rumax', models.DecimalField(decimal_places=2, max_digits=10)), + ('rvmin', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpy', models.DecimalField(decimal_places=2, max_digits=10)), + ('It', models.DecimalField(decimal_places=2, max_digits=10)), + ('Source', models.CharField(max_length=100)), + ('Type', models.CharField(max_length=100, null=True)), + ], + options={ + 'db_table': 'Angles', + }, + ), + migrations.CreateModel( + name='Beams', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Designation', models.CharField(max_length=50)), + ('Mass', models.DecimalField(decimal_places=2, max_digits=10)), + ('Area', models.DecimalField(decimal_places=2, max_digits=10)), + ('D', models.DecimalField(decimal_places=2, max_digits=10)), + ('B', models.DecimalField(decimal_places=2, max_digits=10)), + ('tw', models.DecimalField(decimal_places=2, max_digits=10)), + ('T', models.DecimalField(decimal_places=2, max_digits=10)), + ('FlangeSlope', models.IntegerField()), + ('R1', models.DecimalField(decimal_places=2, max_digits=10)), + ('R2', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iy', models.DecimalField(decimal_places=2, max_digits=10)), + ('rz', models.DecimalField(decimal_places=2, max_digits=10)), + ('ry', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpy', models.DecimalField(decimal_places=2, max_digits=10)), + ('It', models.DecimalField(decimal_places=2, max_digits=10, null=True)), + ('Iw', models.DecimalField(decimal_places=2, max_digits=10, null=True)), + ('Source', models.CharField(max_length=100)), + ('Type', models.CharField(max_length=100, null=True)), + ], + options={ + 'db_table': 'Beams', + }, + ), + migrations.CreateModel( + name='Bolt', + fields=[ + ('id', models.IntegerField(primary_key=True, serialize=False, unique=True)), + ('Bolt_diameter', models.TextField()), + ], + options={ + 'db_table': 'Bolt', + }, + ), + migrations.CreateModel( + name='Bolt_fy_fu', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Property_Class', models.DecimalField(decimal_places=2, max_digits=10)), + ('Diameter_min', models.IntegerField()), + ('Diameter_max', models.IntegerField()), + ('fy', models.IntegerField()), + ('fu', models.IntegerField()), + ], + options={ + 'db_table': 'Bolt_fy_fu', + }, + ), + migrations.CreateModel( + name='Channels', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Designation', models.CharField(max_length=50)), + ('Mass', models.DecimalField(decimal_places=2, max_digits=10)), + ('Area', models.DecimalField(decimal_places=2, max_digits=10)), + ('D', models.DecimalField(decimal_places=2, max_digits=10)), + ('B', models.DecimalField(decimal_places=2, max_digits=10)), + ('tw', models.DecimalField(decimal_places=2, max_digits=10)), + ('T', models.DecimalField(decimal_places=2, max_digits=10)), + ('FlangeSlope', models.IntegerField()), + ('R1', models.DecimalField(decimal_places=2, max_digits=10)), + ('R2', models.DecimalField(decimal_places=2, max_digits=10)), + ('Cy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iy', models.DecimalField(decimal_places=2, max_digits=10)), + ('rz', models.DecimalField(decimal_places=2, max_digits=10)), + ('ry', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpy', models.DecimalField(decimal_places=2, max_digits=10)), + ('It', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iw', models.DecimalField(decimal_places=2, max_digits=10)), + ('Source', models.CharField(max_length=100)), + ('Type', models.CharField(max_length=100, null=True)), + ], + options={ + 'db_table': 'Channels', + }, + ), + migrations.CreateModel( + name='CHS', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Designation', models.CharField(max_length=50)), + ('NB', models.CharField(max_length=50)), + ('OD', models.DecimalField(decimal_places=2, max_digits=10)), + ('T', models.DecimalField(decimal_places=2, max_digits=10)), + ('W', models.DecimalField(decimal_places=2, max_digits=10)), + ('A', models.DecimalField(decimal_places=2, max_digits=10)), + ('V', models.DecimalField(decimal_places=2, max_digits=10)), + ('Ves', models.DecimalField(decimal_places=2, max_digits=10)), + ('Vis', models.DecimalField(decimal_places=2, max_digits=10)), + ('I', models.DecimalField(decimal_places=2, max_digits=10)), + ('Z', models.DecimalField(decimal_places=2, max_digits=10)), + ('R', models.DecimalField(decimal_places=2, max_digits=10)), + ('Rsq', models.DecimalField(decimal_places=2, max_digits=10)), + ('Source', models.CharField(max_length=50)), + ], + options={ + 'db_table': 'CHS', + }, + ), + migrations.CreateModel( + name='Columns', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Designation', models.CharField(max_length=50)), + ('Mass', models.DecimalField(decimal_places=2, max_digits=10)), + ('Area', models.DecimalField(decimal_places=2, max_digits=10)), + ('D', models.DecimalField(decimal_places=2, max_digits=10)), + ('B', models.DecimalField(decimal_places=2, max_digits=10)), + ('tw', models.DecimalField(decimal_places=2, max_digits=10)), + ('T', models.DecimalField(decimal_places=2, max_digits=10)), + ('FlangeSlope', models.IntegerField()), + ('R1', models.DecimalField(decimal_places=2, max_digits=10)), + ('R2', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iy', models.DecimalField(decimal_places=2, max_digits=10)), + ('rz', models.DecimalField(decimal_places=2, max_digits=10)), + ('ry', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpy', models.DecimalField(decimal_places=2, max_digits=10)), + ('It', models.DecimalField(decimal_places=2, max_digits=10, null=True)), + ('Iw', models.DecimalField(decimal_places=2, max_digits=10, null=True)), + ('Source', models.CharField(max_length=100)), + ('Type', models.CharField(max_length=100, null=True)), + ], + options={ + 'db_table': 'Columns', + }, + ), + migrations.CreateModel( + name='CustomMaterials', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('email', models.TextField()), + ('Grade', models.TextField()), + ('Yield_Stress_less_than_20', models.IntegerField(db_column='Yield Stress (< 20)')), + ('Yield_Stress_between_20_and_neg40', models.IntegerField(db_column='Yield Stress (20 -40)')), + ('Yield_Stress_greater_than_40', models.IntegerField(db_column='Yield Stress (> 40)')), + ('Ultimate_Tensile_Stress', models.IntegerField(db_column='Ultimate Tensile Stress')), + ('Elongation', models.IntegerField(blank=True, db_column='Elongation ')), + ], + options={ + 'db_table': 'CustomMaterials', + }, + ), + migrations.CreateModel( + name='Design', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('cookie_id', models.CharField(max_length=32, unique=True)), + ('module_id', models.CharField(max_length=200)), + ('input_values', models.JSONField(blank=True)), + ('logs', models.TextField(blank=True)), + ('output_values', models.JSONField(blank=True)), + ('design_status', models.BooleanField(blank=True)), + ('cad_design_status', models.BooleanField(blank=True)), + ], + options={ + 'db_table': 'Design', + }, + ), + migrations.CreateModel( + name='EqualAngle', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Designation', models.CharField(max_length=50)), + ('Mass', models.DecimalField(decimal_places=2, max_digits=10)), + ('Area', models.DecimalField(decimal_places=2, max_digits=10)), + ('a', models.DecimalField(decimal_places=2, max_digits=10)), + ('b', models.DecimalField(decimal_places=2, max_digits=10)), + ('t', models.DecimalField(decimal_places=2, max_digits=10)), + ('R1', models.DecimalField(decimal_places=2, max_digits=10)), + ('R2', models.DecimalField(decimal_places=2, max_digits=10)), + ('Cz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Cy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Alpha', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iu_max', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iv_min', models.DecimalField(decimal_places=2, max_digits=10)), + ('rz', models.DecimalField(decimal_places=2, max_digits=10)), + ('ry', models.DecimalField(decimal_places=2, max_digits=10)), + ('ru_max', models.DecimalField(decimal_places=2, max_digits=10)), + ('rv_min', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Source', models.CharField(max_length=50)), + ('It', models.DecimalField(decimal_places=2, max_digits=10)), + ], + options={ + 'db_table': 'EqualAngle', + }, + ), + migrations.CreateModel( + name='Material', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Grade', models.TextField()), + ('Yield_Stress_less_than_20', models.IntegerField(db_column='Yield Stress (< 20)')), + ('Yield_Stress_between_20_and_neg40', models.IntegerField(db_column='Yield Stress (20 -40)')), + ('Yield_Stress_greater_than_40', models.IntegerField(db_column='Yield Stress (> 40)')), + ('Ultimate_Tensile_Stress', models.IntegerField(db_column='Ultimate Tensile Stress')), + ('Elongation', models.IntegerField(blank=True, db_column='Elongation ')), + ], + options={ + 'db_table': 'Material', + }, + ), + migrations.CreateModel( + name='RHS', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Designation', models.CharField(max_length=50)), + ('D', models.DecimalField(decimal_places=2, max_digits=10)), + ('B', models.DecimalField(decimal_places=2, max_digits=10)), + ('T', models.DecimalField(decimal_places=2, max_digits=10)), + ('W', models.DecimalField(decimal_places=2, max_digits=10)), + ('A', models.DecimalField(decimal_places=2, max_digits=10)), + ('Izz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iyy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Rzz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Ryy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zzz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zyy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Source', models.CharField(max_length=50)), + ], + options={ + 'db_table': 'RHS', + }, + ), + migrations.CreateModel( + name='SHS', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Designation', models.CharField(max_length=50)), + ('D', models.DecimalField(decimal_places=2, max_digits=10)), + ('B', models.DecimalField(decimal_places=2, max_digits=10)), + ('T', models.DecimalField(decimal_places=2, max_digits=10)), + ('W', models.DecimalField(decimal_places=2, max_digits=10)), + ('A', models.DecimalField(decimal_places=2, max_digits=10)), + ('Izz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iyy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Rzz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Ryy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zzz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zyy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Source', models.CharField(max_length=50)), + ], + options={ + 'db_table': 'SHS', + }, + ), + migrations.CreateModel( + name='UnequalAngle', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Designation', models.CharField(max_length=50)), + ('Mass', models.DecimalField(decimal_places=2, max_digits=10)), + ('Area', models.DecimalField(decimal_places=2, max_digits=10)), + ('a', models.DecimalField(decimal_places=2, max_digits=10)), + ('b', models.DecimalField(decimal_places=2, max_digits=10)), + ('t', models.DecimalField(decimal_places=2, max_digits=10)), + ('R1', models.DecimalField(decimal_places=2, max_digits=10)), + ('R2', models.DecimalField(decimal_places=2, max_digits=10)), + ('Cz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Cy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Alpha', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iu_max', models.DecimalField(decimal_places=2, max_digits=10)), + ('Iv_min', models.DecimalField(decimal_places=2, max_digits=10)), + ('rz', models.DecimalField(decimal_places=2, max_digits=10)), + ('ry', models.DecimalField(decimal_places=2, max_digits=10)), + ('ru_max', models.DecimalField(decimal_places=2, max_digits=10)), + ('rv_min', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpz', models.DecimalField(decimal_places=2, max_digits=10)), + ('Zpy', models.DecimalField(decimal_places=2, max_digits=10)), + ('Source', models.CharField(max_length=50)), + ('It', models.DecimalField(decimal_places=2, max_digits=10)), + ], + options={ + 'db_table': 'UnequalAngle', + }, + ), + migrations.CreateModel( + name='UserAccount', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('username', models.TextField(blank=True, unique=True)), + ('email', models.TextField(blank=True, unique=True)), + ('allInputValueFiles', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(blank=True), size=None)), + ], + options={ + 'db_table': 'UserAccount', + }, + ), + ] diff --git a/backend/apps/core/migrations/0002_userproject_usermoduleactivity.py b/backend/apps/core/migrations/0002_userproject_usermoduleactivity.py new file mode 100644 index 000000000..c70ae3e59 --- /dev/null +++ b/backend/apps/core/migrations/0002_userproject_usermoduleactivity.py @@ -0,0 +1,55 @@ +# Generated by Django 4.2.21 on 2025-06-24 11:49 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='UserProject', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('project_name', models.CharField(max_length=255)), + ('module_type', models.CharField(max_length=100)), + ('module_name', models.CharField(max_length=100)), + ('sub_module', models.CharField(blank=True, max_length=100)), + ('file_path', models.TextField()), + ('file_content', models.JSONField(blank=True, null=True)), + ('description', models.TextField(blank=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('last_accessed', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='projects', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'UserProject', + 'ordering': ['-last_accessed'], + }, + ), + migrations.CreateModel( + name='UserModuleActivity', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('module_name', models.CharField(max_length=100)), + ('module_type', models.CharField(max_length=100)), + ('sub_module', models.CharField(blank=True, max_length=100)), + ('activity_type', models.CharField(choices=[('visit', 'Module Visit'), ('design', 'Design Calculation'), ('save', 'Project Save')], max_length=50)), + ('last_accessed', models.DateTimeField(auto_now=True)), + ('access_count', models.IntegerField(default=1)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='module_activities', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'UserModuleActivity', + 'ordering': ['-last_accessed'], + 'unique_together': {('user', 'module_name', 'module_type', 'sub_module')}, + }, + ), + ] diff --git a/backend/apps/core/migrations/0003_userproject_input_fields_userproject_last_opened.py b/backend/apps/core/migrations/0003_userproject_input_fields_userproject_last_opened.py new file mode 100644 index 000000000..338fa3ee6 --- /dev/null +++ b/backend/apps/core/migrations/0003_userproject_input_fields_userproject_last_opened.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.21 on 2025-06-24 13:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_userproject_usermoduleactivity'), + ] + + operations = [ + migrations.AddField( + model_name='userproject', + name='input_fields', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='userproject', + name='last_opened', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/backend/apps/core/migrations/0004_userproject_calculation_timestamp_and_more.py b/backend/apps/core/migrations/0004_userproject_calculation_timestamp_and_more.py new file mode 100644 index 000000000..57ea2fc13 --- /dev/null +++ b/backend/apps/core/migrations/0004_userproject_calculation_timestamp_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2.21 on 2025-06-24 13:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_userproject_input_fields_userproject_last_opened'), + ] + + operations = [ + migrations.AddField( + model_name='userproject', + name='calculation_timestamp', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='userproject', + name='design_logs', + field=models.TextField(blank=True), + ), + migrations.AddField( + model_name='userproject', + name='design_status', + field=models.CharField(choices=[('draft', 'Draft'), ('input_entered', 'Input Entered'), ('calculating', 'Calculating'), ('completed', 'Completed'), ('error', 'Error')], default='draft', max_length=50), + ), + migrations.AddField( + model_name='userproject', + name='input_data', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='userproject', + name='output_data', + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/backend/apps/core/migrations/0005_add_unique_constraint_userproject.py b/backend/apps/core/migrations/0005_add_unique_constraint_userproject.py new file mode 100644 index 000000000..f2644f7d6 --- /dev/null +++ b/backend/apps/core/migrations/0005_add_unique_constraint_userproject.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.21 on 2025-06-24 17:47 + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0004_userproject_calculation_timestamp_and_more'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='userproject', + unique_together={('user', 'project_name')}, + ), + ] diff --git a/backend/apps/core/migrations/0006_project_alter_userproject_unique_together_and_more.py b/backend/apps/core/migrations/0006_project_alter_userproject_unique_together_and_more.py new file mode 100644 index 000000000..c8c5dceed --- /dev/null +++ b/backend/apps/core/migrations/0006_project_alter_userproject_unique_together_and_more.py @@ -0,0 +1,46 @@ +# Generated by Django 4.2.21 on 2025-07-04 20:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_add_unique_constraint_userproject'), + ] + + operations = [ + migrations.CreateModel( + name='Project', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('module_id', models.CharField(max_length=200)), + ('module_name', models.CharField(max_length=200)), + ('input_values', models.JSONField(blank=True, null=True)), + ('output_values', models.JSONField(blank=True, null=True)), + ('logs', models.JSONField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('user_email', models.CharField(blank=True, max_length=200, null=True)), + ], + options={ + 'db_table': 'Project', + 'ordering': ['-updated_at'], + }, + ), + migrations.AlterUniqueTogether( + name='userproject', + unique_together=None, + ), + migrations.RemoveField( + model_name='userproject', + name='user', + ), + migrations.DeleteModel( + name='UserModuleActivity', + ), + migrations.DeleteModel( + name='UserProject', + ), + ] diff --git a/backend/apps/core/migrations/0007_osifile_remove_project_input_values_and_more.py b/backend/apps/core/migrations/0007_osifile_remove_project_input_values_and_more.py new file mode 100644 index 000000000..becc14a4f --- /dev/null +++ b/backend/apps/core/migrations/0007_osifile_remove_project_input_values_and_more.py @@ -0,0 +1,55 @@ +# Generated by Django 5.2.6 on 2025-09-23 12:53 + +import django.core.files.storage +import pathlib +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0006_project_alter_userproject_unique_together_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='OsiFile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file', models.FileField(storage=django.core.files.storage.FileSystemStorage(base_url='/osifiles/', location=pathlib.PureWindowsPath('D:/Coding/FOSSEE/osdag_project/Osdag-web/osifiles')), upload_to='')), + ('owner_email', models.CharField(blank=True, max_length=200, null=True)), + ('original_name', models.CharField(blank=True, max_length=255, null=True)), + ('size_bytes', models.BigIntegerField(blank=True, null=True)), + ('content_type', models.CharField(blank=True, max_length=100, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'db_table': 'OsiFile', + }, + ), + migrations.RemoveField( + model_name='project', + name='input_values', + ), + migrations.RemoveField( + model_name='project', + name='logs', + ), + migrations.RemoveField( + model_name='project', + name='module_id', + ), + migrations.RemoveField( + model_name='project', + name='module_name', + ), + migrations.RemoveField( + model_name='project', + name='output_values', + ), + migrations.AddField( + model_name='project', + name='osi_file_path', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/backend/apps/core/migrations/0008_project_inputs_json_project_module_project_submodule.py b/backend/apps/core/migrations/0008_project_inputs_json_project_module_project_submodule.py new file mode 100644 index 000000000..5130a9c25 --- /dev/null +++ b/backend/apps/core/migrations/0008_project_inputs_json_project_module_project_submodule.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.25 on 2025-10-31 07:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + + ('core', '0007_osifile_remove_project_input_values_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='inputs_json', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='project', + name='module', + field=models.CharField(blank=True, max_length=200, null=True), + ), + migrations.AddField( + model_name='project', + name='submodule', + field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/backend/apps/core/migrations/0009_useraccount_user_alter_osifile_file_and_more.py b/backend/apps/core/migrations/0009_useraccount_user_alter_osifile_file_and_more.py new file mode 100644 index 000000000..5bfa21cef --- /dev/null +++ b/backend/apps/core/migrations/0009_useraccount_user_alter_osifile_file_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.27 on 2026-01-09 15:50 + +from django.conf import settings +import django.core.files.storage +from django.db import migrations, models +import django.db.models.deletion +import pathlib + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('core', '0008_project_inputs_json_project_module_project_submodule'), + ] + + operations = [ + migrations.AddField( + model_name='useraccount', + name='user', + field=models.ForeignKey(blank=True, help_text='Link to Django User (username = Firebase UID)', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_account', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='osifile', + name='file', + field=models.FileField(storage=django.core.files.storage.FileSystemStorage(base_url='/osifiles/', location=pathlib.PurePosixPath('/home/sogalabhi/Osdag-web/osifiles')), upload_to=''), + ), + migrations.AlterField( + model_name='useraccount', + name='username', + field=models.TextField(blank=True, help_text='Firebase UID', unique=True), + ), + ] diff --git a/APP_CRASH/Appcrash/_forms/__init__.py b/backend/apps/core/migrations/__init__.py similarity index 100% rename from APP_CRASH/Appcrash/_forms/__init__.py rename to backend/apps/core/migrations/__init__.py diff --git a/backend/apps/core/models.py b/backend/apps/core/models.py new file mode 100644 index 000000000..5f3545d36 --- /dev/null +++ b/backend/apps/core/models.py @@ -0,0 +1,424 @@ +from django.db import models + +# postgres imports +from django.contrib.postgres.fields import ArrayField + +# other imports +from django.contrib.auth.hashers import make_password, check_password +from django.conf import settings +from django.core.files.storage import FileSystemStorage + +# Dedicated storage for .osi files +osi_file_storage = FileSystemStorage( + location=getattr(settings, 'OSIFILES_ROOT', ''), + base_url=getattr(settings, 'OSIFILES_URL', '/osifiles/') +) + +class Project(models.Model): + """Project object to store minimal project info and file location.""" + name = models.CharField(max_length=200) + # Optional module and submodule identifiers for routing/reporting + + module = models.CharField(max_length=200, blank=True, null=True) + submodule = models.CharField(max_length=200, blank=True, null=True) + # Canonical inputs persisted as JSON + inputs_json = models.JSONField(blank=True, null=True) + osi_file_path = models.TextField(blank=True, null=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + user_email = models.CharField(max_length=200, blank=True, null=True) + + class Meta: + app_label = 'core' + db_table = "Project" + ordering = ['-updated_at'] # Most recent first + + def __str__(self): + return f"{self.name}" + + +class OsiFile(models.Model): + """Stores uploaded .osi files with their storage URL and timestamps.""" + file = models.FileField(upload_to='', storage=osi_file_storage) + owner_email = models.CharField(max_length=200, blank=True, null=True) + original_name = models.CharField(max_length=255, blank=True, null=True) + size_bytes = models.BigIntegerField(blank=True, null=True) + content_type = models.CharField(max_length=100, blank=True, null=True) + created_at = models.DateTimeField(auto_now_add=True) + + class Meta: + app_label = 'core' + db_table = "OsiFile" + + def __str__(self): + return self.file.name + +class Design(models.Model): + """Design Session object in Database.""" + cookie_id = models.CharField(unique=True, max_length=32) + module_id = models.CharField(max_length=200) + input_values = models.JSONField(blank=True) + logs = models.TextField(blank=True) + output_values = models.JSONField(blank=True) + design_status = models.BooleanField(blank=True) + cad_design_status = models.BooleanField(blank=True) + + class Meta : + app_label = 'core' + db_table = "Design" + + +######################################################### +# Author : Atharva Pingale ( FOSSEE Summer Fellow '23 ) # +######################################################### +class UserAccount(models.Model): + """ + User account model for storing user-specific data. + + IMPORTANT: With Firebase Authentication: + - username field stores Firebase UID (not a traditional username) + - user field is a ForeignKey to Django User model (which also uses Firebase UID as username) + - email field stores the user's email address + - No password field needed (authentication handled by Firebase) + + The User model (Django's built-in) stores: + - username = Firebase UID + - email = User's email address + """ + # ForeignKey to Django User model (which uses Firebase UID as username) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name='user_account', + null=True, + blank=True, + help_text="Link to Django User (username = Firebase UID)" + ) + # Firebase UID stored as username (for backward compatibility and direct lookups) + username = models.TextField(blank=True, unique=True, help_text="Firebase UID") + email = models.TextField(blank=True, unique=True) + allInputValueFiles = ArrayField(models.TextField(blank=True)) + + class Meta: + app_label = 'core' + db_table = "UserAccount" + + def __str__(self): + return f"UserAccount: {self.username or self.email or 'No identifier'}" + +class Anchor_Bolt(models.Model): + Diameter = models.TextField() + + class Meta: + app_label = 'core' + db_table = "Anchor_Bolt" + + +class Angle_Pitch(models.Model): + Nominal_Leg = models.IntegerField() + Max_Bolt_Dia = models.IntegerField() + Bolt_lines = models.IntegerField() + S1 = models.IntegerField(null=True) + S2 = models.IntegerField(null=True) + S3 = models.IntegerField(null=True) + + class Meta: + app_label = 'core' + db_table = "Angle_Pitch" + + +class Angles(models.Model): + Designation = models.CharField(max_length=50) + Mass = models.DecimalField(max_digits=10, decimal_places=2) + Area = models.DecimalField(max_digits=10, decimal_places=2) + a = models.DecimalField(max_digits=10, decimal_places=2) + b = models.DecimalField(max_digits=10, decimal_places=2) + t = models.DecimalField(max_digits=10, decimal_places=2) + R1 = models.DecimalField(max_digits=10, decimal_places=2) + R2 = models.DecimalField(max_digits=10, decimal_places=2) + Cz = models.DecimalField(max_digits=10, decimal_places=2) + Cy = models.DecimalField(max_digits=10, decimal_places=2) + Iz = models.DecimalField(max_digits=10, decimal_places=2) + Iy = models.DecimalField(max_digits=10, decimal_places=2) + Alpha = models.DecimalField(max_digits=10, decimal_places=2) + lumax = models.DecimalField(max_digits=10, decimal_places=2) + lvmin = models.DecimalField(max_digits=10, decimal_places=2) + rz = models.DecimalField(max_digits=10, decimal_places=2) + ry = models.DecimalField(max_digits=10, decimal_places=2) + rumax = models.DecimalField(max_digits=10, decimal_places=2) + rvmin = models.DecimalField(max_digits=10, decimal_places=2) + Zz = models.DecimalField(max_digits=10, decimal_places=2) + Zy = models.DecimalField(max_digits=10, decimal_places=2) + Zpz = models.DecimalField(max_digits=10, decimal_places=2) + Zpy = models.DecimalField(max_digits=10, decimal_places=2) + It = models.DecimalField(max_digits=10, decimal_places=2) + Source = models.CharField(max_length=100) + Type = models.CharField(max_length=100, null=True) + + class Meta: + app_label = 'core' + db_table = "Angles" + + +class Beams(models.Model): + Designation = models.CharField(max_length=50) + Mass = models.DecimalField(max_digits=10, decimal_places=2) + Area = models.DecimalField(max_digits=10, decimal_places=2) + D = models.DecimalField(max_digits=10, decimal_places=2) + B = models.DecimalField(max_digits=10, decimal_places=2) + tw = models.DecimalField(max_digits=10, decimal_places=2) + T = models.DecimalField(max_digits=10, decimal_places=2) + FlangeSlope = models.IntegerField() + R1 = models.DecimalField(max_digits=10, decimal_places=2) + R2 = models.DecimalField(max_digits=10, decimal_places=2) + Iz = models.DecimalField(max_digits=10, decimal_places=2) + Iy = models.DecimalField(max_digits=10, decimal_places=2) + rz = models.DecimalField(max_digits=10, decimal_places=2) + ry = models.DecimalField(max_digits=10, decimal_places=2) + Zz = models.DecimalField(max_digits=10, decimal_places=2) + Zy = models.DecimalField(max_digits=10, decimal_places=2) + Zpz = models.DecimalField(max_digits=10, decimal_places=2) + Zpy = models.DecimalField(max_digits=10, decimal_places=2) + It = models.DecimalField(null=True, max_digits=10, decimal_places=2) + Iw = models.DecimalField(null=True, max_digits=10, decimal_places=2) + Source = models.CharField(max_length=100) + Type = models.CharField(null=True, max_length=100) + + class Meta: + app_label = 'core' + db_table = "Beams" + + +class Bolt(models.Model): + id = models.IntegerField(primary_key=True, unique=True) + Bolt_diameter = models.TextField() + + class Meta: + app_label = 'core' + db_table = "Bolt" + + +class Bolt_fy_fu(models.Model): + Property_Class = models.DecimalField(max_digits=10, decimal_places=2) + Diameter_min = models.IntegerField() + Diameter_max = models.IntegerField() + fy = models.IntegerField() + fu = models.IntegerField() + + class Meta: + app_label = 'core' + db_table = "Bolt_fy_fu" + + +class CHS(models.Model): + Designation = models.CharField(max_length=50) + NB = models.CharField(max_length=50) + OD = models.DecimalField(max_digits=10, decimal_places=2) + T = models.DecimalField(max_digits=10, decimal_places=2) + W = models.DecimalField(max_digits=10, decimal_places=2) + A = models.DecimalField(max_digits=10, decimal_places=2) + V = models.DecimalField(max_digits=10, decimal_places=2) + Ves = models.DecimalField(max_digits=10, decimal_places=2) + Vis = models.DecimalField(max_digits=10, decimal_places=2) + I = models.DecimalField(max_digits=10, decimal_places=2) + Z = models.DecimalField(max_digits=10, decimal_places=2) + R = models.DecimalField(max_digits=10, decimal_places=2) + Rsq = models.DecimalField(max_digits=10, decimal_places=2) + Source = models.CharField(max_length=50) + + class Meta: + app_label = 'core' + db_table = "CHS" + + +class Channels(models.Model): + Designation = models.CharField(max_length=50) + Mass = models.DecimalField(max_digits=10, decimal_places=2) + Area = models.DecimalField(max_digits=10, decimal_places=2) + D = models.DecimalField(max_digits=10, decimal_places=2) + B = models.DecimalField(max_digits=10, decimal_places=2) + tw = models.DecimalField(max_digits=10, decimal_places=2) + T = models.DecimalField(max_digits=10, decimal_places=2) + FlangeSlope = models.IntegerField() + R1 = models.DecimalField(max_digits=10, decimal_places=2) + R2 = models.DecimalField(max_digits=10, decimal_places=2) + Cy = models.DecimalField(max_digits=10, decimal_places=2) + Iz = models.DecimalField(max_digits=10, decimal_places=2) + Iy = models.DecimalField(max_digits=10, decimal_places=2) + rz = models.DecimalField(max_digits=10, decimal_places=2) + ry = models.DecimalField(max_digits=10, decimal_places=2) + Zz = models.DecimalField(max_digits=10, decimal_places=2) + Zy = models.DecimalField(max_digits=10, decimal_places=2) + Zpz = models.DecimalField(max_digits=10, decimal_places=2) + Zpy = models.DecimalField(max_digits=10, decimal_places=2) + It = models.DecimalField(max_digits=10, decimal_places=2) + Iw = models.DecimalField(max_digits=10, decimal_places=2) + Source = models.CharField(max_length=100) + Type = models.CharField(null=True, max_length=100) + + class Meta: + app_label = 'core' + db_table = "Channels" + + +class Columns(models.Model): + Designation = models.CharField(max_length=50) + Mass = models.DecimalField(max_digits=10, decimal_places=2) + Area = models.DecimalField(max_digits=10, decimal_places=2) + D = models.DecimalField(max_digits=10, decimal_places=2) + B = models.DecimalField(max_digits=10, decimal_places=2) + tw = models.DecimalField(max_digits=10, decimal_places=2) + T = models.DecimalField(max_digits=10, decimal_places=2) + FlangeSlope = models.IntegerField() + R1 = models.DecimalField(max_digits=10, decimal_places=2) + R2 = models.DecimalField(max_digits=10, decimal_places=2) + Iz = models.DecimalField(max_digits=10, decimal_places=2) + Iy = models.DecimalField(max_digits=10, decimal_places=2) + rz = models.DecimalField(max_digits=10, decimal_places=2) + ry = models.DecimalField(max_digits=10, decimal_places=2) + Zz = models.DecimalField(max_digits=10, decimal_places=2) + Zy = models.DecimalField(max_digits=10, decimal_places=2) + Zpz = models.DecimalField(max_digits=10, decimal_places=2) + Zpy = models.DecimalField(max_digits=10, decimal_places=2) + It = models.DecimalField(null=True, max_digits=10, decimal_places=2) + Iw = models.DecimalField(null=True, max_digits=10, decimal_places=2) + Source = models.CharField(max_length=100) + Type = models.CharField(null=True, max_length=100) + + class Meta: + app_label = 'core' + db_table = "Columns" + + +class EqualAngle(models.Model): + Designation = models.CharField(max_length=50) + Mass = models.DecimalField(max_digits=10, decimal_places=2) + Area = models.DecimalField(max_digits=10, decimal_places=2) + a = models.DecimalField(max_digits=10, decimal_places=2) + b = models.DecimalField(max_digits=10, decimal_places=2) + t = models.DecimalField(max_digits=10, decimal_places=2) + R1 = models.DecimalField(max_digits=10, decimal_places=2) + R2 = models.DecimalField(max_digits=10, decimal_places=2) + Cz = models.DecimalField(max_digits=10, decimal_places=2) + Cy = models.DecimalField(max_digits=10, decimal_places=2) + Iz = models.DecimalField(max_digits=10, decimal_places=2) + Iy = models.DecimalField(max_digits=10, decimal_places=2) + Alpha = models.DecimalField(max_digits=10, decimal_places=2) + Iu_max = models.DecimalField(max_digits=10, decimal_places=2) + Iv_min = models.DecimalField(max_digits=10, decimal_places=2) + rz = models.DecimalField(max_digits=10, decimal_places=2) + ry = models.DecimalField(max_digits=10, decimal_places=2) + ru_max = models.DecimalField(max_digits=10, decimal_places=2) + rv_min = models.DecimalField(max_digits=10, decimal_places=2) + Zz = models.DecimalField(max_digits=10, decimal_places=2) + Zy = models.DecimalField(max_digits=10, decimal_places=2) + Zpz = models.DecimalField(max_digits=10, decimal_places=2) + Zpy = models.DecimalField(max_digits=10, decimal_places=2) + Source = models.CharField(max_length=50) + It = models.DecimalField(max_digits=10, decimal_places=2) + + class Meta: + app_label = 'core' + db_table = "EqualAngle" + + +class UnequalAngle(models.Model): + Designation = models.CharField(max_length=50) + Mass = models.DecimalField(max_digits=10, decimal_places=2) + Area = models.DecimalField(max_digits=10, decimal_places=2) + a = models.DecimalField(max_digits=10, decimal_places=2) + b = models.DecimalField(max_digits=10, decimal_places=2) + t = models.DecimalField(max_digits=10, decimal_places=2) + R1 = models.DecimalField(max_digits=10, decimal_places=2) + R2 = models.DecimalField(max_digits=10, decimal_places=2) + Cz = models.DecimalField(max_digits=10, decimal_places=2) + Cy = models.DecimalField(max_digits=10, decimal_places=2) + Iz = models.DecimalField(max_digits=10, decimal_places=2) + Iy = models.DecimalField(max_digits=10, decimal_places=2) + Alpha = models.DecimalField(max_digits=10, decimal_places=2) + Iu_max = models.DecimalField(max_digits=10, decimal_places=2) + Iv_min = models.DecimalField(max_digits=10, decimal_places=2) + rz = models.DecimalField(max_digits=10, decimal_places=2) + ry = models.DecimalField(max_digits=10, decimal_places=2) + ru_max = models.DecimalField(max_digits=10, decimal_places=2) + rv_min = models.DecimalField(max_digits=10, decimal_places=2) + Zz = models.DecimalField(max_digits=10, decimal_places=2) + Zy = models.DecimalField(max_digits=10, decimal_places=2) + Zpz = models.DecimalField(max_digits=10, decimal_places=2) + Zpy = models.DecimalField(max_digits=10, decimal_places=2) + Source = models.CharField(max_length=50) + It = models.DecimalField(max_digits=10, decimal_places=2) + + class Meta: + app_label = 'core' + db_table = "UnequalAngle" + + +class Material(models.Model): + Grade = models.TextField() + Yield_Stress_less_than_20 = models.IntegerField(db_column="Yield Stress (< 20)") + Yield_Stress_between_20_and_neg40 = models.IntegerField(db_column="Yield Stress (20 -40)") + Yield_Stress_greater_than_40 = models.IntegerField(db_column="Yield Stress (> 40)") + Ultimate_Tensile_Stress = models.IntegerField(db_column="Ultimate Tensile Stress") + Elongation = models.IntegerField(db_column="Elongation ", blank=True) + + class Meta: + app_label = 'core' + db_table = "Material" + +class CustomMaterials(models.Model): + email = models.TextField() + Grade = models.TextField() + Yield_Stress_less_than_20 = models.IntegerField(db_column="Yield Stress (< 20)") + Yield_Stress_between_20_and_neg40 = models.IntegerField(db_column="Yield Stress (20 -40)") + Yield_Stress_greater_than_40 = models.IntegerField(db_column="Yield Stress (> 40)") + Ultimate_Tensile_Stress = models.IntegerField(db_column="Ultimate Tensile Stress") + Elongation = models.IntegerField(db_column="Elongation ", blank=True) + + class Meta: + app_label = 'core' + db_table = "CustomMaterials" + + +class RHS(models.Model): + Designation = models.CharField(max_length=50) + D = models.DecimalField(max_digits=10, decimal_places=2) + B = models.DecimalField(max_digits=10, decimal_places=2) + T = models.DecimalField(max_digits=10, decimal_places=2) + W = models.DecimalField(max_digits=10, decimal_places=2) + A = models.DecimalField(max_digits=10, decimal_places=2) + Izz = models.DecimalField(max_digits=10, decimal_places=2) + Iyy = models.DecimalField(max_digits=10, decimal_places=2) + Rzz = models.DecimalField(max_digits=10, decimal_places=2) + Ryy = models.DecimalField(max_digits=10, decimal_places=2) + Zzz = models.DecimalField(max_digits=10, decimal_places=2) + Zyy = models.DecimalField(max_digits=10, decimal_places=2) + Zpz = models.DecimalField(max_digits=10, decimal_places=2) + Zpy = models.DecimalField(max_digits=10, decimal_places=2) + Source = models.CharField(max_length=50) + + class Meta: + app_label = 'core' + db_table = "RHS" + + +class SHS(models.Model): + Designation = models.CharField(max_length=50) + D = models.DecimalField(max_digits=10, decimal_places=2) + B = models.DecimalField(max_digits=10, decimal_places=2) + T = models.DecimalField(max_digits=10, decimal_places=2) + W = models.DecimalField(max_digits=10, decimal_places=2) + A = models.DecimalField(max_digits=10, decimal_places=2) + Izz = models.DecimalField(max_digits=10, decimal_places=2) + Iyy = models.DecimalField(max_digits=10, decimal_places=2) + Rzz = models.DecimalField(max_digits=10, decimal_places=2) + Ryy = models.DecimalField(max_digits=10, decimal_places=2) + Zzz = models.DecimalField(max_digits=10, decimal_places=2) + Zyy = models.DecimalField(max_digits=10, decimal_places=2) + Zpz = models.DecimalField(max_digits=10, decimal_places=2) + Zpy = models.DecimalField(max_digits=10, decimal_places=2) + Source = models.CharField(max_length=50) + + class Meta: + app_label = 'core' + db_table = "SHS" diff --git a/backend/apps/core/module_finder.py b/backend/apps/core/module_finder.py new file mode 100644 index 000000000..38fa17f59 --- /dev/null +++ b/backend/apps/core/module_finder.py @@ -0,0 +1,230 @@ +""" +Dynamic Module Finder - Auto-discovers modules from parent module registries +Replaces the old osdag_api/module_finder.py with registry-based discovery +""" +import importlib +import pkgutil +from typing import Dict, Any, List, Protocol +from types import ModuleType + + +class ModuleApiType(Protocol): + """Protocol for module API interface (backward compatibility)""" + + def validate_input(self, input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + pass + + def get_required_keys(self) -> List[str]: + """Return all required input parameters for the module""" + pass + + def create_module(self) -> Any: + """Create an instance of the module design class and set it up for use""" + pass + + def create_from_input(self, input_values: Dict[str, Any]) -> Any: + """Create an instance of the module design class from input values.""" + pass + + def generate_output(self, input_values: Dict[str, Any]) -> Dict[str, Any]: + """ + Generate, format and return the output values from the given input values. + Output format (json): { + "Bolt.Pitch": { + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)", + "value": 40 + } + } + """ + pass + + def create_cad_model(self, input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + pass + + +class ModuleApiAdapter: + """ + Adapter class that wraps adapter module functions to provide ModuleApiType interface + This allows backward compatibility with code that expects the old module API + """ + + def __init__(self, adapter_module: ModuleType): + """ + Initialize adapter with the adapter module + + Args: + adapter_module: The adapter module (e.g., apps.modules.shear_connection.submodules.fin_plate.adapter) + """ + self._adapter = adapter_module + + def validate_input(self, input_values: Dict[str, Any]) -> None: + """Delegate to adapter's validate_input function""" + return self._adapter.validate_input(input_values) + + def get_required_keys(self) -> List[str]: + """Delegate to adapter's get_required_keys function""" + return self._adapter.get_required_keys() + + def create_module(self) -> Any: + """Delegate to adapter's create_module function""" + return self._adapter.create_module() + + def create_from_input(self, input_values: Dict[str, Any]) -> Any: + """Delegate to adapter's create_from_input function""" + return self._adapter.create_from_input(input_values) + + def generate_output(self, input_values: Dict[str, Any]) -> Dict[str, Any]: + """Delegate to adapter's generate_output function""" + return self._adapter.generate_output(input_values) + + def create_cad_model(self, input_values: Dict[str, Any], section: str, session: str) -> str: + """Delegate to adapter's create_cad_model function""" + return self._adapter.create_cad_model(input_values, section, session) + + +# Global module dictionary - auto-populated on import +_module_dict: Dict[str, ModuleApiType] = {} + + +def _discover_modules(): + """ + Auto-discover all modules from parent module registries + Scans apps.modules.*.submodules.* for MODULE_ID and creates adapter wrappers + Uses file reading to avoid import-time dependency issues + """ + import os + import re + from pathlib import Path + + # Get the apps/modules directory + backend_path = Path(__file__).resolve().parent.parent.parent + modules_path = backend_path / 'apps' / 'modules' + + if not modules_path.exists(): + print("Warning: modules path does not exist:", modules_path) + return + + # Iterate through all parent module directories + for parent_module_dir in modules_path.iterdir(): + if not parent_module_dir.is_dir() or parent_module_dir.name.startswith('_'): + continue + + submodules_path = parent_module_dir / 'submodules' + if not submodules_path.exists(): + continue + + parent_module_name = parent_module_dir.name + package_base = f'apps.modules.{parent_module_name}.submodules' + + # Iterate through all sub-module directories + for submodule_dir in submodules_path.iterdir(): + if not submodule_dir.is_dir() or submodule_dir.name.startswith('_'): + continue + + submodule_name = submodule_dir.name + init_file = submodule_dir / '__init__.py' + adapter_file = submodule_dir / 'adapter.py' + + # Skip if adapter doesn't exist + if not adapter_file.exists(): + continue + + # Read MODULE_ID from __init__.py without importing + module_id = None + if init_file.exists(): + try: + content = init_file.read_text(encoding='utf-8') + # Look for MODULE_ID = '...' pattern + match = re.search(r"MODULE_ID\s*=\s*['\"]([^'\"]+)['\"]", content) + if match: + module_id = match.group(1) + except Exception as e: + # If file reading fails, try importing (may fail due to dependencies) + try: + init_module_path = f'{package_base}.{submodule_name}' + init_module = importlib.import_module(init_module_path) + if hasattr(init_module, 'MODULE_ID'): + module_id = init_module.MODULE_ID + except: + pass + + if not module_id: + continue + + # Import the adapter module (lazy - only when needed) + adapter_module_path = f'{package_base}.{submodule_name}.adapter' + try: + adapter_module = importlib.import_module(adapter_module_path) + except ImportError as e: + # Adapter import failed - skip for now, will fall back to old system + continue + except Exception as e: + # Other errors - skip + continue + + # Create adapter wrapper + try: + adapter = ModuleApiAdapter(adapter_module) + _module_dict[module_id] = adapter + print(f"✅ Discovered module: {module_id} from {parent_module_name}/{submodule_name}") + except Exception as e: + print(f"Warning: Error creating adapter for {module_id}: {e}") + continue + + +# Auto-discover modules on import +_discover_modules() + + +def get_module_api(module_id: str) -> ModuleApiType: + """ + Return the API adapter for the specified module by MODULE_ID + Falls back to old osdag_api for modules not yet migrated + + Args: + module_id: The MODULE_ID (e.g., 'FinPlateConnection', 'Beam-Beam-End-Plate-Connection') + + Returns: + ModuleApiType adapter instance + + Raises: + KeyError: If module_id is not found in either new or old system + """ + # Try new registry-based discovery first + if module_id in _module_dict: + return _module_dict[module_id] + + # Fall back to old osdag_api for modules not yet migrated + try: + from osdag_api.module_finder import get_module_api as old_get_module_api + return old_get_module_api(module_id) + except (ImportError, KeyError): + raise KeyError( + f"Module '{module_id}' not found. " + f"Available modules: {list(_module_dict.keys())}" + ) + + +# Export module_dict for backward compatibility (used by modules_api.py) +module_dict = _module_dict + + +# For backward compatibility with old imports +# Combine discovered modules with old modules (for modules not yet migrated) +def _get_developed_modules(): + """Get list of all available modules (new + old)""" + new_modules = list(_module_dict.keys()) + try: + from osdag_api.module_finder import module_dict as old_module_dict + old_modules = list(old_module_dict.keys()) + # Combine and deduplicate + all_modules = list(set(new_modules + old_modules)) + return all_modules + except ImportError: + return new_modules + +developed_modules = _get_developed_modules() + diff --git a/backend/apps/core/permissions.py b/backend/apps/core/permissions.py new file mode 100644 index 000000000..3eb837b9f --- /dev/null +++ b/backend/apps/core/permissions.py @@ -0,0 +1,30 @@ +""" +Custom permissions for Django REST Framework +""" + +from rest_framework import permissions + + +class IsEmailVerified(permissions.BasePermission): + """ + Permission class to check if user's email is verified. + Requires Firebase authentication middleware to set request.email_verified. + """ + + def has_permission(self, request, view): + """ + Check if user is authenticated and email is verified. + + Returns: + bool: True if user is authenticated and email is verified, False otherwise + """ + # Check if user is authenticated + if not request.user or not request.user.is_authenticated: + return False + + # Check if email_verified was set by FirebaseAuthentication middleware + # This attribute is set in firebase_auth.py middleware + email_verified = getattr(request, 'email_verified', False) + + return email_verified + diff --git a/backend/apps/core/registry.py b/backend/apps/core/registry.py new file mode 100644 index 000000000..dd8488192 --- /dev/null +++ b/backend/apps/core/registry.py @@ -0,0 +1,50 @@ +""" +Base Module Registry - DRY principle for all parent module registries +""" +import importlib +import pkgutil +from typing import Dict, Type + + +class BaseModuleRegistry: + """Base registry class for auto-discovering sub-modules""" + _registry: Dict[str, Type] = {} # Maps slug -> Service class + _module_id_map: Dict[str, str] = {} # Maps MODULE_ID -> slug + + @classmethod + def register(cls, slug: str, module_id: str, service_class: Type): + """Register a sub-module by URL slug and MODULE_ID""" + cls._registry[slug] = service_class + cls._module_id_map[module_id] = slug + + @classmethod + def get_service_by_slug(cls, slug: str): + """Get service class by URL slug (e.g., 'fin-plate')""" + return cls._registry.get(slug) + + @classmethod + def get_service_by_module_id(cls, module_id: str): + """Get service class by MODULE_ID (e.g., 'FinPlateConnection')""" + slug = cls._module_id_map.get(module_id) + return cls._registry.get(slug) if slug else None + + @classmethod + def auto_discover(cls, package_name: str, package_path: str): + """Auto-discover sub-modules in a package""" + print(f"[auto_discover] Starting for {package_name} at {package_path}") + for _, name, _ in pkgutil.iter_modules([package_path]): + print(f"[auto_discover] Found module: {name}") + try: + mod = importlib.import_module(f'{package_name}.{name}') + print(f"[auto_discover] Imported {name}, has MODULE_ID: {hasattr(mod, 'MODULE_ID')}, has Service: {hasattr(mod, 'Service')}") + if hasattr(mod, 'MODULE_ID') and hasattr(mod, 'Service'): + # Convert module name to slug (e.g., 'fin_plate' -> 'fin-plate') + slug = name.replace('_', '-') + cls.register(slug, mod.MODULE_ID, mod.Service) + print(f"[auto_discover] Registered: {slug}") + except ImportError as e: + print(f"[auto_discover] ImportError for {name}: {e}") + import traceback + traceback.print_exc() + continue + diff --git a/backend/apps/core/serializers.py b/backend/apps/core/serializers.py new file mode 100644 index 000000000..a3aa83bad --- /dev/null +++ b/backend/apps/core/serializers.py @@ -0,0 +1,153 @@ +# DRF imports +from rest_framework import serializers + +# importing models +from apps.core.models import Anchor_Bolt , Angle_Pitch , Angles , Beams , Bolt , Bolt_fy_fu , CHS , Channels , Columns , EqualAngle , UnequalAngle , Material , RHS , SHS, CustomMaterials +from apps.core.models import Design, UserAccount, OsiFile + +######################################################### +# Author : Atharva Pingale ( FOSSEE Summer Fellow '23 ) # +######################################################### + +# Removed: MyTokenObtainPairSerializer - No longer needed with Firebase authentication + +class UserAccount_Serializer(serializers.ModelSerializer): + class Meta: + model = UserAccount + fields = ['username', 'email', 'allInputValueFiles'] # No password! + + def create(self, validated_data): + return UserAccount.objects.create(**validated_data) + + def update(self, instance, validated_data): + # Only update allowed fields + instance.username = validated_data.get('username', instance.username) + instance.email = validated_data.get('email', instance.email) + instance.allInputValueFiles = validated_data.get('allInputValueFiles', instance.allInputValueFiles) + instance.save() + return instance + + +class OsiFileSerializer(serializers.ModelSerializer): + + class Meta: + model = OsiFile + fields = ['id', 'file', 'created_at'] + + +class Design_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Design + fields = '__all__' + + def create(self, validated_data) : + # creating an instance of the Design model + return Design.objects.create(**validated_data) + + def update(self, instance, validated_data) : + # update the input_values field of the instance + instance.input_values = validated_data.get('input_values' , instance.input_values) + + # save the instance + instance.save() + + return instance + + +class Anchor_Bolt_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Anchor_Bolt + fields = '__all__' + +class Angle_Pitch_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Angle_Pitch + fields = '__all__' + +class Angles_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Angles + fields = '__all__' + + +class Beams_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Beams + fields = '__all__' + + +class Bolt_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Bolt + fields = '__all__' + + +class Bolt_fy_fu_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Bolt_fy_fu + fields = '__all__' + +class CHS_Serializer(serializers.ModelSerializer) : + + class Meta : + model = CHS + fields = '__all__' + +class Channels_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Channels + fields = '__all__' + +class Columns_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Columns + fields = '__all__' + +class EqualAngle_Serializer(serializers.ModelSerializer) : + + class Meta : + model = EqualAngle + fields = '__all__' + +class UnequalAngle_Serializer(serializers.ModelSerializer) : + + class Meta : + model = UnequalAngle + fields = '__all__' + +class Material_Serializer(serializers.ModelSerializer) : + + class Meta : + model = Material + fields = '__all__' + +class CustomMaterials_Serializer(serializers.ModelSerializer): + + class Meta : + model = CustomMaterials + fields = '__all__' + + +class RHS_Serializer(serializers.ModelSerializer) : + + class Meta : + model = RHS + fields = '__all__' + + +class SHS_Serializer(serializers.ModelSerializer) : + + class Meta : + model = SHS + fields = '__all__' + + diff --git a/APP_CRASH/Appcrash/formatters/__init__.py b/backend/apps/core/tests/__init__.py similarity index 100% rename from APP_CRASH/Appcrash/formatters/__init__.py rename to backend/apps/core/tests/__init__.py diff --git a/backend/apps/core/tests/test_api.py b/backend/apps/core/tests/test_api.py new file mode 100644 index 000000000..a22a99f5e --- /dev/null +++ b/backend/apps/core/tests/test_api.py @@ -0,0 +1,220 @@ +from django.test import TestCase +from django.conf import settings +import json + +# importing data +from ..data.design_types import design_type_data, connections_data, shear_connection, moment_connection, b2b_splice, b2column, c2c_splice, base_plate, tension_member + +######################################################### +# Author : Atharva Pingale ( FOSSEE Summer Fellow '23 ) # +######################################################### + + +# Create your tests here. +class APITest(TestCase) : + + def test_setUp(self) : + static_url = '/static/' + self.assertEqual(settings.STATIC_URL , static_url) + + allow_origin = True + self.assertEqual(settings.CORS_ORIGIN_ALLOW_ALL , allow_origin) + + + ### TESTING MAIN WINDOW API ### + """ VALID """ + def test_valid_window(self) : + response = self.client.get("/osdag-web/") + response = response.json() + self.assertEqual(list(response.keys())[0] , 'result') + + response = json.dumps(response) + self.assertJSONEqual(response , {'result' : design_type_data}) + + """ INVALID """ + def test_invalid_window(self) : + response = self.client.get("/osdag-web") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag_web/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag_web") + self.assertTrue(response.status_code!=200) # 301 + + + + ### TESTING MODULES API ### + """ VALID """ + def test_valid_connection(self) : + response = self.client.get("/osdag-web/connections") + response = response.json() + self.assertEqual(list(response.keys())[0] , 'result') + + response = json.dumps(response) + self.assertJSONEqual(response , {'result' : connections_data}) + + """ INVALID """ + def test_invalid_connection(self) : + response = self.client.get("/osdag-web/connections/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connection/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connection") + self.assertTrue(response.status_code!=200) # 301 + + """ VALID """ + def test_valid_tension_member(self) : + response = self.client.get("/osdag-web/tension-member") + response = response.json() + self.assertEqual(list(response.keys())[0] , 'result') + response = json.dumps(response) + self.assertJSONEqual(response , {'result' : tension_member}) + + """ INVALID """ + def test_invalid_tension_member(self) : + response = self.client.get("/osdag-web/tension-member/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/tension_member") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/tension_member/") + self.assertTrue(response.status_code!=200) # 301 + + + ### TESTING OF CONNECTION SUB MODULES ### + # 1. shear-connection + # 2. moment-connection + # 3. base-plate + ######################################### + """ VALID """ + def test_valid_shear_connection(self) : + response = self.client.get("/osdag-web/connections/shear-connection") + response = response.json() + self.assertEqual(list(response.keys())[0] , 'result') + response = json.dumps(response) + self.assertJSONEqual(response , {'result' : shear_connection}) + + """ INVALID """ + def test_invalid_shear_connection(self) : + response = self.client.get("/osdag-web/connections/shear-connection/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/shear_connections") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/shear_connection") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/shear-connections") + self.assertTrue(response.status_code!=200) # 301 + + """ VALID """ + def test_valid_moment_connection(self) : + response = self.client.get("/osdag-web/connections/moment-connection") + response = response.json() + self.assertEqual(list(response.keys())[0] , 'result') + response = json.dumps(response) + self.assertJSONEqual(response , {'result' : moment_connection}) + + """ INVALID """ + def test_invalid_moment_connection(self) : + response = self.client.get("/osdag-web/connections/moment-connection/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/moment-connections") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/moment_connection") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/moment_connections") + self.assertTrue(response.status_code!=200) # 301 + + """ VALID """ + def test_valid_connection_base_plate(self) : + response = self.client.get("/osdag-web/connections/base-plate") + response = response.json() + self.assertEqual(list(response.keys())[0] , 'result') + response = json.dumps(response) + self.assertJSONEqual(response , {'result' : base_plate}) + + """ INVALID """ + def test_invalid_connection_base_plate(self) : + response = self.client.get("/osdag-web/connections/base-plate/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/base-plates") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/base_plate") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/base-plates") + self.assertTrue(response.status_code!=200) # 301 + + + ### TESTING OF MODULE CONNECTION SUB MODULES ### + # 1. beam-to-beam-splice + # 2. beam-to-column + # 3. column-to-column-splice + ################################################ + """ VALID """ + def test_valid_moment_connection_beam_to_beam_splice(self) : + response = self.client.get("/osdag-web/connections/moment-connection/beam-to-beam-splice") + response = response.json() + self.assertEqual(list(response.keys())[0] , 'result') + response = json.dumps(response) + self.assertJSONEqual(response , {'result' : b2b_splice}) + + """ INVALID """ + def test_invalid_moment_connection_beam_to_beam_splice(self) : + response = self.client.get("/osdag-web/connections/moment-connection/beam-to-beam-splice/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/moment-connection/beam_to_beam_splice") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/moment-connection/beam_to_beam_splice/") + self.assertTrue(response.status_code!=200) # 301 + + """ VALID """ + def test_valid_moment_connection_beam_to_column(self) : + response = self.client.get("/osdag-web/connections/moment-connection/beam-to-column") + response = response.json() + self.assertEqual(list(response.keys())[0] , 'result') + response = json.dumps(response) + self.assertJSONEqual(response , {'result' : b2column}) + + """ INVALID """ + def test_invalid_moment_connection_beam_to_column(self) : + response = self.client.get("/osdag-web/connections/moment-connection/beam-to-column/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/moment-connection/beam_to_column") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/moment-connection/beam_to_column/") + self.assertTrue(response.status_code!=200) # 301 + + """ VALID """ + def test_valid_moment_connection_column_to_column_splice(self) : + response = self.client.get("/osdag-web/connections/moment-connection/column-to-column-splice") + response = response.json() + self.assertEqual(list(response.keys())[0] , 'result') + response = json.dumps(response) + self.assertJSONEqual(response , {'result' : c2c_splice}) + + """ INVALID """ + def test_invalid_moment_connection_column_to_column_splice(self) : + response = self.client.get("/osdag-web/connections/moment-connection/column-to-column-splice/") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/moment-connection/column_to_column_splice") + self.assertTrue(response.status_code!=200) # 301 + + response = self.client.get("/osdag-web/connections/moment-connection/column_to_column_splice/") + self.assertTrue(response.status_code!=200) # 301 diff --git a/backend/apps/core/urls.py b/backend/apps/core/urls.py new file mode 100644 index 000000000..446063747 --- /dev/null +++ b/backend/apps/core/urls.py @@ -0,0 +1,71 @@ +""" +Core app URLs - Non-module specific endpoints +Extracted from osdag/urls.py +""" +from django.urls import path +# Import from new api structure (using re-exports from __init__.py) +from apps.core.api import ( + ObtainInputFileView, SaveInputFileView, + JWTHomeView, GoogleSSOView, + ProjectAPI, ProjectDetailAPI, ProjectByNameAPI, + SaveOsiFromInputs, OpenOsiUpload, OpenOsiById, ModuleRoutes, ProjectOsiDownload, + ParseReportSections, CustomizeReport, + DesignPreference, MaterialDetails, CompanyLogoView, + CADGeneration, CADDownload +) +from apps.core import views + +app_name = 'core' + +urlpatterns = [ + # Design preferences + path('api/design-preferences/', DesignPreference.as_view(), name="design-pref"), + path('api/materialDetails/', MaterialDetails.as_view()), + path('api/company-logo/', CompanyLogoView.as_view()), + + # Authentication and authorization + path('jwt/home', JWTHomeView.as_view()), # view for testing purpose + path('googlesso/', GoogleSSOView.as_view()), + path('api/auth/firebase-login/', views.FirebaseAuthView.as_view(), name="firebase_auth"), + path('api/dashboard/', views.dashboard_view, name='dashboard'), + + # User URLs + path('api/user/saveinput/', SaveInputFileView.as_view()), + path('api/user/obtain-input-file/', ObtainInputFileView.as_view()), + + # OSI upload via DRF (multipart form-data) + path('api/save-osi/', SaveInputFileView.as_view()), + + # Project management URLs + path('api/projects/', ProjectAPI.as_view(), name='projects'), + path('api/projects//', ProjectDetailAPI.as_view(), name='project-detail'), + path('api/projects/by-name//', ProjectByNameAPI.as_view(), name='project-by-name'), + + # OSI endpoints + path('api/save-osi-from-inputs/', SaveOsiFromInputs.as_view()), + path('api/open-osi/', OpenOsiUpload.as_view()), + path('api/open-osi//', OpenOsiById.as_view()), + path('api/module-routes/', ModuleRoutes.as_view()), + path('api/projects//osi', ProjectOsiDownload.as_view()), + + # Report customization API endpoints + path('api/report/parse-sections/', ParseReportSections.as_view(), name='parse-report-sections'), + path('api/report/customize/', CustomizeReport.as_view(), name='customize-report'), + + # CAD generation & download (new backend handlers) + path('api/design/cad', CADGeneration.as_view()), + path('api/design/cad/', CADGeneration.as_view()), + path('api/design/downloadCad', CADDownload.as_view()), + path('api/design/downloadCad/', CADDownload.as_view()), + + # Legacy design type views (temporary - may be moved later) + path('osdag-web/', views.get_design_types, name='index'), + path('osdag-web/connections', views.get_connections, name='connections'), + path('osdag-web/connections/shear-connection', views.get_shear_connection, name='shear-connection'), + path('osdag-web/connections/moment-connection', views.get_moment_connection, name='moment_connection'), + path('osdag-web/connections/moment-connection/beam-to-beam-splice', views.get_b2b_splice, name='beam-to-beam-splice'), + path('osdag-web/connections/moment-connection/beam-to-column', views.get_b2column, name='beam-to-column'), + path('osdag-web/connections/moment-connection/column-to-column-splice', views.get_c2c_splice, name='column-to-column-splice'), + path('osdag-web/connections/base-plate', views.get_base_plate, name='base-plate'), + path('osdag-web/tension-member', views.get_tension_member, name='tension-member'), +] diff --git a/backend/apps/core/utils/REPORT_IMAGE_GENERATION.md b/backend/apps/core/utils/REPORT_IMAGE_GENERATION.md new file mode 100644 index 000000000..9ca2d9b82 --- /dev/null +++ b/backend/apps/core/utils/REPORT_IMAGE_GENERATION.md @@ -0,0 +1,267 @@ + +## Problem Statement + +When generating design reports via the web API, CAD images (3D, front, top, side views) were either: +1. **Missing** - Causing LaTeX compilation errors (`File 'None' not found`) +2. **Colorless/White** - Images were generated but appeared as blank white canvases without any geometry or colors +3. **Single-color** - Images showed geometry but without the distinct colors for different parts (beams, columns, plates, bolts, welds) that users see in the GUI + +### Root Causes + +1. **Missing Image Generation**: The CLI approach (`save_design()`) expects images to already exist, but the web API wasn't generating them before calling `save_design()`. + +2. **Incorrect Rendering Method**: Initial attempts used VTK or Open3D rendering, which: + - Don't support the colored rendering used by the GUI + - Produce single-color or grayscale images + - Don't replicate the exact visual appearance of the GUI + +3. **BREP File Approach**: Loading pre-generated BREP files directly loses color information, as colors are applied during CAD generation, not stored in BREP files. + +## Solution + +The solution replicates the exact GUI rendering approach using: +- **Qt/PySide6** with OpenCASCADE display (same as GUI) +- **Module-based CAD generation** - Uses the module's own `call_3DModel()` and `display_3DModel()` methods +- **Headless rendering** - Uses Qt's offscreen platform plugin with Xvfb + +### Architecture + +``` +Web API Request + ↓ +generate_cad_images_for_report() + ↓ +generate_with_opencascade_display() [PRIMARY - WITH COLORS] + ├─ Setup headless Qt (QT_QPA_PLATFORM=offscreen) + ├─ Create offscreen display (pyside6 backend) + ├─ Create CommonDesignLogic instance + ├─ Call module.call_3DModel() → Creates CAD with colored parts + ├─ Call display_3DModel() → Displays with colors + ├─ Set white background for reports + └─ Export 4 views: 3d.png, front.png, top.png, side.png + ↓ +[If Qt fails] +generate_with_vtk() [FALLBACK - NO COLORS] + └─ Single-color rendering +``` + +## Setup Requirements + +### System Packages + +#### 1. Xvfb (X Virtual Framebuffer) +Required for headless Qt rendering. + +```bash +sudo apt-get update +sudo apt-get install xvfb +``` + +**Why needed**: Qt's offscreen platform plugin requires a display server. Xvfb provides a virtual display for headless environments. + +#### 2. libxcb-cursor0 +Required for Qt's xcb platform plugin (even when using offscreen). + +```bash +sudo apt-get install libxcb-cursor0 +``` + +**Why needed**: Qt 6.5+ requires this library for the xcb platform plugin, even when using offscreen rendering. + +### Python Packages + +All required packages should already be in `requirements.txt`: + +- **PySide6** - Qt Python bindings (already installed) +- **pythonocc-core** - OpenCASCADE Python bindings (already installed) +- **vtk** - VTK library (fallback rendering, already installed) + +### Verification + +Check if Xvfb is installed: +```bash +which Xvfb +``` + +Check if libxcb-cursor0 is installed: +```bash +dpkg -l | grep libxcb-cursor +``` + +## How It Works + +### 1. Module CAD Generation + +Instead of loading pre-generated BREP files, the solution uses the module's own CAD generation logic: + +```python +# Create CommonDesignLogic (same as GUI) +comm_logic = CommonDesignLogic( + display=off_display, + cad_widget=mock_widget, + folder=folder, + connection=connection, + mainmodule=mainmodule +) + +# Call module's CAD generation (creates colored parts) +comm_logic.call_3DModel(design_status, module) + +# Display with colors (like GUI) +comm_logic.display_3DModel("Model", "gradient_light") +``` + +This ensures: +- **Individual parts are created** with their respective colors (beams, plates, bolts, welds) +- **Colors match the GUI** exactly +- **No BREP conversion needed** - colors are applied during generation + +### 2. Headless Qt Setup + +```python +# Set Qt to use offscreen platform (must be before Qt imports) +os.environ['QT_QPA_PLATFORM'] = 'offscreen' + +# Create offscreen display with pyside6 backend +off_display, _, _, _ = init_display_off_screen(backend_str='pyside6') +``` + +### 3. Image Export + +After displaying the model with colors: + +```python +# Set white background for reports +off_display.set_bg_gradient_color([255, 255, 255], [126, 126, 126]) + +# Export 4 views +off_display.ExportToImage('3d.png') # 3D view +off_display.View_Front() +off_display.FitAll() +off_display.Repaint() +off_display.ExportToImage('front.png') # Front view +# ... (top.png, side.png) +``` + +## File Structure + +``` +Osdag-web/ +├── backend/ +│ └── apps/ +│ └── core/ +│ └── utils/ +│ └── report_image_generator.py # Main implementation +├── osdag_core/ +│ ├── cad/ +│ │ └── common_logic.py # CommonDesignLogic (modified for headless) +│ └── texlive/ +│ └── Design_wrapper.py # Display initialization +└── backend/ + └── file_storage/ + └── design_report/ + └── ResourceFiles/ + └── images/ # Generated images stored here + ├── 3d.png + ├── front.png + ├── top.png + └── side.png +``` + +## Key Code Changes + +### 1. `report_image_generator.py` + +- **`generate_with_opencascade_display()`**: Primary rendering function using Qt/OpenCASCADE +- **Module-based CAD generation**: Uses `CommonDesignLogic.call_3DModel()` instead of BREP loading +- **Fallback chain**: Qt → VTK → Open3D → broken.png + +### 2. `common_logic.py` + +- **Optional cleanup coordinator**: Made `osdag_gui.OS_safety_protocols` import optional for headless mode +- **Headless-compatible**: Works without GUI dependencies + +### 3. `Design_wrapper.py` + +- **Removed verbose print**: Cleaned up debug output + +## Troubleshooting + +### Issue: "Xvfb not found" +**Solution**: Install Xvfb (see Setup Requirements) + +### Issue: "Could not load the Qt platform plugin 'xcb'" +**Solution**: Install `libxcb-cursor0` (see Setup Requirements) + +### Issue: "'Viewer3d' object has no attribute 'Update'" +**Solution**: Use `Repaint()` instead of `Update()` for OpenCASCADE display objects. + +### Issue: Images are still white/colorless +**Check**: +1. Is Qt rendering being used? Check logs for `[REPORT_IMG] ATTEMPTING Qt/OpenCASCADE RENDERING` +2. Is `call_3DModel()` succeeding? Check logs for `connectivityObj exists` +3. Are colors being applied? Check that `display_3DModel()` is called with "Model" component + +### Issue: "Module CAD generation failed" +**Possible causes**: +1. Module object is None or invalid +2. Module doesn't have required attributes (`design_status`, `module`, `mainmodule`) +3. CAD generation function doesn't exist for this module type + +**Solution**: Ensure module is properly initialized before calling `generate_cad_images_for_report()` + +## Testing + +### Manual Test + +1. Generate a report via web API +2. Check generated images in `backend/file_storage/design_report/ResourceFiles/images/` +3. Verify: + - Images exist (3d.png, front.png, top.png, side.png) + - Images show geometry (not blank) + - Images have colors (beams, plates, bolts in different colors) + - Colors match GUI appearance + +### Automated Test + +```python +from backend.apps.core.utils.report_image_generator import generate_cad_images_for_report + +# Create module instance +module = create_from_input(input_values) + +# Generate images +result = generate_cad_images_for_report(module, output_dir) + +# Verify +assert result['success'] == True +assert '3d' in result['images'] +assert os.path.exists(result['images']['3d']) +``` + +## Performance Considerations + +- **Qt rendering**: ~2-5 seconds per report (with colors) +- **VTK fallback**: ~1-2 seconds per report (no colors) +- **Image generation**: Happens synchronously before `save_design()` call + +## Future Improvements + +1. **Caching**: Cache generated images if design parameters haven't changed +2. **Async generation**: Generate images asynchronously to improve API response time +3. **Image optimization**: Compress images or use different formats for smaller file sizes +4. **Error recovery**: Better fallback handling and error messages + +## Related Files + +- `backend/apps/core/utils/report_image_generator.py` - Main implementation +- `backend/apps/core/api/design/design_report_csv_view.py` - API endpoint that calls image generation +- `osdag_core/design_report/reportGenerator_latex.py` - LaTeX report generator (uses images) +- `osdag_core/cad/common_logic.py` - CAD generation logic (used by both GUI and web) + +## References + +- OpenCASCADE Documentation: https://dev.opencascade.org/ +- Qt Offscreen Rendering: https://doc.qt.io/qt-6/qoffscreensurface.html +- PySide6 Documentation: https://doc.qt.io/qtforpython/ + diff --git a/backend/apps/core/utils/__init__.py b/backend/apps/core/utils/__init__.py new file mode 100644 index 000000000..24d0eb2d4 --- /dev/null +++ b/backend/apps/core/utils/__init__.py @@ -0,0 +1,22 @@ +""" +Core utilities package - Re-exports for convenience +""" +from .errors import OsdagApiException, MissingKeyError, InvalidInputTypeError +from .validation import ( + validate_list_type, contains_keys, custom_list_validation, + int_able, float_able, is_yes_or_no, + validate_string, validate_num, validate_arr +) +from .mesh_export import write_stl + +__all__ = [ + # Errors + 'OsdagApiException', 'MissingKeyError', 'InvalidInputTypeError', + # Validation utilities + 'validate_list_type', 'contains_keys', 'custom_list_validation', + 'int_able', 'float_able', 'is_yes_or_no', + 'validate_string', 'validate_num', 'validate_arr', + # Mesh export + 'write_stl', +] + diff --git a/backend/apps/core/utils/cad_helpers.py b/backend/apps/core/utils/cad_helpers.py new file mode 100644 index 000000000..1c497e120 --- /dev/null +++ b/backend/apps/core/utils/cad_helpers.py @@ -0,0 +1,298 @@ +""" +CAD Generation Helper Functions +Extracted from legacy cad_model_api.py for reuse in new module-based endpoints +""" +import os +import uuid +import base64 +import json as json_module +from typing import Dict, List, Any, Optional + + +# Sections mapping for each module/submodule +SECTION_MAPPINGS = { + 'shear-connection': { + 'fin-plate': ['Beam', 'Column', 'Plate'], + 'cleat-angle': ['Model', 'Beam', 'Column', 'cleatAngle'], + 'end-plate': ['Model', 'Beam', 'Column', 'Plate'], + 'seated-angle': ['Model', 'Beam', 'Column', 'SeatedAngle'], + }, + 'moment-connection': { + 'beam-beam-cover-plate-bolted': ['Model', 'Beam', 'CoverPlate'], + 'beam-beam-cover-plate-welded': ['Model', 'Beam', 'CoverPlate'], + 'beam-beam-end-plate': ['Model', 'Beam', 'EndPlate'], + 'beam-column-end-plate': ['Model', 'Beam', 'Column', 'Connector'], + 'column-column-cover-plate-bolted': ['Model', 'Column', 'CoverPlate'], + 'column-column-cover-plate-welded': ['Model', 'Column', 'CoverPlate'], + 'column-column-end-plate': ['Model', 'Column', 'Connector'], + }, + 'simple-connection': { + 'lap-joint-bolted': ['Model', 'Plate 1', 'Plate 2', 'Bolts'], + 'lap-joint-welded': ['Model', 'Plate 1', 'Plate 2', 'Welds'], + 'butt-joint-bolted': ['Model', 'Plate 1', 'Plate 2', 'Cover Plate', 'Bolts'], + 'butt-joint-welded': ['Model', 'Plate 1', 'Plate 2', 'Cover Plate', 'Welds'], + }, + 'tension-member': { + 'bolted': ['Model', 'Member', 'Plate', 'Endplate'], + 'welded': ['Model', 'Member', 'Plate', 'Endplate'], + }, + 'flexure-member': { + 'simply-supported-beam': [], # TODO: Add sections when available + }, + 'base-plate': { + 'base-plate': ['Model', 'Column', 'Plate'], + }, + 'compression-member': { + 'struts-bolted': ['Model', 'Member'], + 'axially-loaded-column': ['Model'], + }, +} + + +def get_default_sections(module_name: str, submodule_slug: str) -> List[str]: + """ + Get default sections for a module/submodule combination. + + Args: + module_name: Parent module name (e.g., 'shear-connection') + submodule_slug: Submodule slug (e.g., 'fin-plate') + + Returns: + List of section names to generate + """ + return SECTION_MAPPINGS.get(module_name, {}).get(submodule_slug, ['Model']) + + +def resolve_file_path(path: str, repo_root: str, backend_root: str) -> tuple[str, str]: + """ + Resolve a relative or absolute file path to an absolute path. + + Args: + path: File path (relative or absolute) + repo_root: Repository root directory + backend_root: Backend root directory + + Returns: + Tuple of (absolute_path, base_root) + """ + if os.path.isabs(path): + return path, repo_root + + # Try repo root first, then backend root + path_repo_root = os.path.join(repo_root, path) + path_backend_root = os.path.join(backend_root, path) + + if os.path.exists(path_repo_root): + return path_repo_root, repo_root + elif os.path.exists(path_backend_root): + return path_backend_root, backend_root + else: + # Default to repo root for error reporting + return path_repo_root, repo_root + + +def generate_cad_models( + service_class, + inputs: Dict[str, Any], + sections: Optional[List[str]] = None, + session_id: Optional[str] = None, + create_from_input_func=None +) -> Dict[str, Any]: + """ + Generate CAD models for given sections using a service class. + + Args: + service_class: Service class with get_cad_model method + inputs: Design input parameters + sections: List of sections to generate (if None, uses default) + session_id: Session identifier (if None, generates UUID) + create_from_input_func: Optional function to create module instance for hover_dict + + Returns: + Dictionary with: + - 'files': Dict mapping section names to base64-encoded file data + - 'hover_dict': Dict mapping part names to hover tooltip text + - 'warnings': List of warning/error dicts + """ + # Get repository and backend roots + current_dir = os.path.dirname(os.path.abspath(__file__)) + repo_root = os.path.abspath(os.path.join(current_dir, "../../../../")) + backend_root = os.path.join(repo_root, "backend") + + # Generate session ID if not provided + if session_id is None: + session_id = str(uuid.uuid4()) + + # Initialize output + output_files = {} + error_details = [] + + # Generate CAD for each section + for section in sections: + print(f"[cad_helpers] Generating section: {section}") + try: + # Check if service has get_cad_model method + if not hasattr(service_class, 'get_cad_model'): + error_details.append({ + "section": section, + "error": "get_cad_model not implemented in service" + }) + continue + + # Call service to generate CAD + print(f"[cad_helpers] Calling get_cad_model for section '{section}'") + path = service_class.get_cad_model(inputs, section, session_id) + + if not path: + print(f'[cad_helpers] Error generating {section}: get_cad_model() returned None or empty string') + error_details.append({ + "section": section, + "error": "get_cad_model returned empty path" + }) + continue + + print(f'[cad_helpers] {section} generated successfully, returned path: {path}') + + # Resolve file path + path_to_file, base_root = resolve_file_path(path, repo_root, backend_root) + print(f"[cad_helpers] Resolved path for section '{section}': {path_to_file}") + + if not os.path.exists(path_to_file): + msg = f'Generated file for {section} does not exist at: {path_to_file}' + print(msg) + error_details.append({"section": section, "error": msg}) + continue + + # Try STL first, fall back to BREP + stl_path = path_to_file.replace(".brep", ".stl") + try: + if os.path.exists(stl_path): + print(f"[cad_helpers] Using STL for section '{section}': {stl_path}") + with open(stl_path, "rb") as f: + b64 = base64.b64encode(f.read()).decode("ascii") + output_files[section] = f"data:application/octet-stream;base64,{b64}" + print(f"[cad_helpers] Loaded STL for {section}") + else: + print(f"[cad_helpers] STL missing for section '{section}', falling back to BREP") + with open(path_to_file, "rb") as f: + b64 = base64.b64encode(f.read()).decode("ascii") + output_files[section] = f"data:application/octet-stream;base64,{b64}" + print(f"[cad_helpers] Loaded BREP for {section}") + + # If this is the merged Model, also try to include per-part STLs from manifest + if section == "Model": + try: + manifest_path = path_to_file.replace(".brep", ".parts.json") + if os.path.exists(manifest_path): + print(f"[cad_helpers] Found manifest at {manifest_path}") + with open(manifest_path, "r", encoding="utf-8") as mf: + manifest = json_module.load(mf) + parts = manifest.get("parts", []) + print(f"[cad_helpers] Manifest parts count: {len(parts)}") + + for entry in parts: + name = entry.get("name") + stl_rel = entry.get("stlPath") + brep_rel = entry.get("brepPath") + if not name: + continue + + # Prefer STL + part_file_abs = None + part_base_roots = [base_root, repo_root] + + if stl_rel: + for root in part_base_roots: + candidate = os.path.join(root, stl_rel) + if os.path.exists(candidate): + part_file_abs = candidate + print(f"[cad_helpers] Part '{name}' using STL {candidate}") + break + + if not part_file_abs and brep_rel: + for root in part_base_roots: + candidate = os.path.join(root, brep_rel) + if os.path.exists(candidate): + part_file_abs = candidate + print(f"[cad_helpers] Part '{name}' using BREP {candidate}") + break + + if part_file_abs: + with open(part_file_abs, "rb") as pf: + b64p = base64.b64encode(pf.read()).decode("ascii") + output_files[name] = f"data:application/octet-stream;base64,{b64p}" + print(f"[cad_helpers] Loaded part {name} from manifest") + except Exception as me: + print(f"[cad_helpers] Failed to load parts from manifest: {me}") + + except Exception as e: + print(f"[cad_helpers] Failed reading model file for {section}: {e}") + error_details.append({ + "section": section, + "error": f"Failed reading file: {str(e)}" + }) + continue + + except Exception as e: + print(f"[cad_helpers] Exception while generating {section}: {e}") + import traceback + traceback.print_exc() + error_details.append({"section": section, "error": str(e)}) + + print(f"[cad_helpers] Final output_files keys: {list(output_files.keys())}") + + # Build hover_dict if create_from_input function is provided + hover_dict = {} + if create_from_input_func: + try: + mdl = create_from_input_func(inputs) + + # Call output_values() to populate hover_dict + if hasattr(mdl, 'output_values') and callable(mdl.output_values): + try: + mdl.output_values(True) + print(f"[cad_helpers] Called output_values(), hover_dict populated") + except Exception as output_err: + print(f"[cad_helpers] Error calling output_values(): {output_err}") + import traceback + traceback.print_exc() + + # Get hover_dict after it's been populated + cand = getattr(mdl, 'hover_dict', None) + if isinstance(cand, dict) and len(cand) > 0: + hover_dict = cand + if "Weld" in hover_dict and "Welds" not in hover_dict: + hover_dict["Welds"] = hover_dict["Weld"] + print(f"[cad_helpers] Retrieved hover_dict: {hover_dict}") + else: + print(f"[cad_helpers] hover_dict is empty or not a dict: {cand}") + # Minimal fallback for Bolt info + bolt_grade = None + bolt_dia = None + try: + grades = inputs.get('Bolt.Grade') or [] + dias = inputs.get('Bolt.Diameter') or [] + if isinstance(grades, list) and grades: + bolt_grade = grades[-1] + if isinstance(dias, list) and dias: + bolt_dia = dias[-1] + except Exception: + pass + if bolt_grade or bolt_dia: + parts = [] + if bolt_grade: + parts.append(f"Grade: {bolt_grade}") + if bolt_dia: + parts.append(f"Diameter: {bolt_dia} mm") + hover_dict['Bolt'] = ' '.join(parts) + except Exception as e: + print(f"[cad_helpers] Error building hover_dict: {e}") + import traceback + traceback.print_exc() + + return { + 'files': output_files, + 'hover_dict': hover_dict, + 'warnings': error_details + } + diff --git a/backend/apps/core/utils/errors.py b/backend/apps/core/utils/errors.py new file mode 100644 index 000000000..7e467ff4a --- /dev/null +++ b/backend/apps/core/utils/errors.py @@ -0,0 +1,100 @@ +"""Define all exceptions used in the Osdag backend.""" +from typing import Dict, Any, List, Optional +from rest_framework import status + + +class OsdagApiException(Exception): + """Super class for all Osdag api exceptions.""" + pass + + +class MissingKeyError(OsdagApiException): + """Raised when a design parameter is missing from the input parameters.""" + def __init__(self, key: str): + super(MissingKeyError, self).__init__("Please specify " + key + " in input values.") + + +class InvalidInputTypeError(OsdagApiException): + """Raised when an input parameter is of wrong type.""" + def __init__(self, key: str, type: str): + super(InvalidInputTypeError, self).__init__("Input Parameter " + key + " should be of type " + type + " .") + + +class RangeValidationError(OsdagApiException): + """Raised when a value is outside allowed range""" + def __init__(self, field: str, value: Any, min_val: Optional[float] = None, + max_val: Optional[float] = None, allow_zero: bool = False): + self.field = field + self.value = value + constraints = [] + if min_val is not None: + if allow_zero: + constraints.append(f"minimum {min_val} (zero allowed)") + else: + constraints.append(f"minimum {min_val} (exclusive)") + if max_val is not None: + constraints.append(f"maximum {max_val}") + constraint_str = " and ".join(constraints) if constraints else "invalid range" + message = f"{field} value {value} violates constraint: {constraint_str}" + super(RangeValidationError, self).__init__(message) + + +class ValidationError(OsdagApiException): + """Raised when input validation fails with multiple errors""" + def __init__(self, errors: List[Dict[str, str]]): + self.errors = errors + message = "Validation failed: " + "; ".join( + [f"{e.get('field', 'unknown')}: {e.get('message', '')}" for e in errors] + ) + super(ValidationError, self).__init__(message) + + +def format_error_response( + error: Exception, + error_type: Optional[str] = None +) -> Dict[str, Any]: + """ + Format any exception into a consistent API error response. + Used by all ViewSets for consistent error handling. + """ + # Determine error type if not provided + if error_type is None: + if isinstance(error, (MissingKeyError, InvalidInputTypeError, ValidationError)): + error_type = "validation_error" + elif isinstance(error, RangeValidationError): + error_type = "range_validation_error" + elif isinstance(error, ValueError): + error_type = "value_error" + else: + error_type = "server_error" + + # Build error details + details = [] + if isinstance(error, ValidationError): + details = error.errors + elif isinstance(error, (MissingKeyError, InvalidInputTypeError)): + details = [{"field": "input", "message": str(error)}] + elif isinstance(error, RangeValidationError): + details = [{"field": error.field, "message": str(error)}] + elif isinstance(error, ValueError): + details = [{"field": "unknown", "message": str(error)}] + + return { + "success": False, + "error": { + "type": error_type, + "message": str(error), + "details": details + } + } + + +def get_error_status_code(error: Exception) -> int: + """Get appropriate HTTP status code for error type""" + if isinstance(error, (MissingKeyError, InvalidInputTypeError, ValidationError, RangeValidationError)): + return status.HTTP_400_BAD_REQUEST + elif isinstance(error, ValueError): + return status.HTTP_422_UNPROCESSABLE_ENTITY + else: + return status.HTTP_500_INTERNAL_SERVER_ERROR + diff --git a/backend/apps/core/utils/mesh_export.py b/backend/apps/core/utils/mesh_export.py new file mode 100644 index 000000000..d22116f66 --- /dev/null +++ b/backend/apps/core/utils/mesh_export.py @@ -0,0 +1,29 @@ +""" +STL mesh export utilities for CAD models. +""" +import os +from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh +from OCC.Core.StlAPI import StlAPI_Writer + + +def write_stl(shape, stl_path: str, linear_deflection: float = 0.3, angular_deflection: float = 0.2, parallel: bool = True) -> str: + """Tessellate a TopoDS_Shape and write it to STL. + + Returns the written STL path on success, raises RuntimeError on failure. + """ + # Ensure output directory exists + out_dir = os.path.dirname(stl_path) + if out_dir and not os.path.exists(out_dir): + os.makedirs(out_dir, exist_ok=True) + + # Perform tessellation + mesh = BRepMesh_IncrementalMesh(shape, linear_deflection, parallel, angular_deflection, parallel) + mesh.Perform() + + # Write STL + writer = StlAPI_Writer() + ok = writer.Write(shape, stl_path) + if not ok: + raise RuntimeError(f"Failed to write STL: {stl_path}") + return stl_path + diff --git a/backend/apps/core/utils/module_helpers.py b/backend/apps/core/utils/module_helpers.py new file mode 100644 index 000000000..42a7585dd --- /dev/null +++ b/backend/apps/core/utils/module_helpers.py @@ -0,0 +1,149 @@ +""" +Shared utilities for module endpoints +Handles authentication, guest mode, and project saving logic +Used by all parent module ViewSets (shear_connection, moment_connection, etc.) +""" +from typing import Optional, Dict, Any +from django.http import HttpRequest +from apps.core.models import Project + + +def is_guest_user(request: HttpRequest) -> bool: + """ + Check if the current user is a guest user. + Guests don't send authentication tokens, so they are unauthenticated. + + Args: + request: Django HTTP request object + + Returns: + bool: True if user is guest, False otherwise + """ + return not (hasattr(request, 'user') and request.user.is_authenticated) + + +def get_user_email(request: HttpRequest) -> Optional[str]: + """ + Get user email from request (JWT token or user object). + + Args: + request: Django HTTP request object + + Returns: + str or None: User email if available, None otherwise + """ + user_email = getattr(request.user, 'email', None) + if not user_email and hasattr(request, 'auth') and isinstance(request.auth, dict): + user_email = request.auth.get('email') + return user_email + + +def save_to_project( + project_id: int, + user_email: str, + inputs: Dict[str, Any], + submodule_slug: str, + module_name: Optional[str] = None +) -> Dict[str, Any]: + """ + Save design inputs to a project in the database. + + Args: + project_id: ID of the project to update + user_email: Email of the project owner (for verification) + inputs: Design input parameters (JSON-serializable dict) + submodule_slug: URL slug of the sub-module (e.g., 'fin-plate') + module_name: Optional parent module name (e.g., 'shear-connection') + + Returns: + dict: Result dictionary with 'saved', 'project_id', and optional 'error' keys + """ + try: + project = Project.objects.get(id=project_id, user_email=user_email) + + # Update project with latest inputs + project.inputs_json = inputs + project.submodule = submodule_slug.replace('-', '_') # Store slug (e.g., 'fin_plate') + + # Optionally update parent module name + if module_name: + project.module = module_name.replace('-', '_') # Store module (e.g., 'shear_connection') + + project.save() + + return { + 'saved': True, + 'project_id': project_id + } + except Project.DoesNotExist: + return { + 'saved': False, + 'project_id': project_id, + 'error': 'Project not found or access denied' + } + except Exception as e: + return { + 'saved': False, + 'project_id': project_id, + 'error': str(e) + } + + +def handle_design_request( + request: HttpRequest, + inputs: Dict[str, Any], + project_id: Optional[int], + submodule_slug: str, + module_name: Optional[str] = None +) -> Dict[str, Any]: + """ + Handle authentication and project saving logic for design requests. + This is a convenience function that combines guest checking and project saving. + + Args: + request: Django HTTP request object + inputs: Design input parameters + project_id: Optional project ID to save to + submodule_slug: URL slug of the sub-module + module_name: Optional parent module name + + Returns: + dict: Context dictionary with: + - 'is_guest': bool + - 'user_email': str or None + - 'project_result': dict (if project_id provided) + """ + is_guest = is_guest_user(request) + user_email = get_user_email(request) if not is_guest else None + + context = { + 'is_guest': is_guest, + 'user_email': user_email, + 'project_result': None + } + + # Handle project saving if project_id provided and user is authenticated + if project_id: + if is_guest: + context['project_result'] = { + 'saved': False, + 'project_id': project_id, + 'error': 'Guest users cannot save to projects' + } + elif user_email: + context['project_result'] = save_to_project( + project_id=project_id, + user_email=user_email, + inputs=inputs, + submodule_slug=submodule_slug, + module_name=module_name + ) + else: + context['project_result'] = { + 'saved': False, + 'project_id': project_id, + 'error': 'User email not found' + } + + return context + diff --git a/backend/apps/core/utils/osi_files.py b/backend/apps/core/utils/osi_files.py new file mode 100644 index 000000000..ebb832dc1 --- /dev/null +++ b/backend/apps/core/utils/osi_files.py @@ -0,0 +1,215 @@ +import json +import re +from datetime import datetime +from typing import Any, Dict, Tuple + +from django.core.files.base import ContentFile + +OSI_SCHEMA_VERSION = "1.0" +SUPPORTED_VERSIONS = {"1.0"} + + +def validate_module_id(module_id: str) -> bool: + if not isinstance(module_id, str): + return False + # allow letters, numbers, dashes and underscores + return bool(re.fullmatch(r"[A-Za-z0-9_-]{2,64}", module_id)) + + +def build_osi_payload(name: str, module_id: str, inputs: Dict[str, Any]) -> Dict[str, Any]: + if not name or not isinstance(name, str): + raise ValueError("Invalid project name") + if not validate_module_id(module_id): + raise ValueError("Invalid module_id") + if not isinstance(inputs, dict): + raise ValueError("Invalid inputs; expected object") + return { + "version": OSI_SCHEMA_VERSION, + "name": name, + "module_id": module_id, + "inputs": inputs, + "meta": { + "saved_at": datetime.utcnow().isoformat() + "Z", + }, + } + + +def _to_legacy_yaml(module_id: str, name: str, inputs: Dict[str, Any]) -> str: + """Render inputs into legacy flat YAML with dotted, title-cased keys. + + This function implements the FinPlateConnection mapping now; for other modules + it will fall back to a generic block under JSON if no mapping is present. + """ + def qt(s: Any) -> str: + # quote list items as strings in YAML as per legacy files + return f"'{str(s)}'" + + legacy: Dict[str, Any] = {} + + # Common top-level entries + legacy["Module"] = inputs.get("module") or module_id + + # FinPlateConnection mapping (expand as needed) + if module_id == "FinPlateConnection": + legacy.update({ + "Bolt.Bolt_Hole_Type": inputs.get("bolt_hole_type"), + "Bolt.Diameter": inputs.get("bolt_diameter") or [], + "Bolt.Grade": inputs.get("bolt_grade") or [], + "Bolt.Slip_Factor": inputs.get("bolt_slip_factor"), + "Bolt.TensionType": inputs.get("bolt_tension_type"), + "Bolt.Type": (inputs.get("bolt_type") or "").replace("_", " ").title().replace("Bearing Bolt", "Bearing Bolt"), + "Connectivity": inputs.get("connectivity") or inputs.get("Connectivity"), + "Connector.Material": inputs.get("connector_material"), + "Design.Design_Method": inputs.get("design_method"), + "Detailing.Corrosive_Influences": inputs.get("detailing_corr_status"), + "Detailing.Edge_type": inputs.get("detailing_edge_type"), + "Detailing.Gap": inputs.get("detailing_gap"), + "Load.Axial": inputs.get("load_axial"), + "Load.Shear": inputs.get("load_shear"), + "Material": inputs.get("material"), + "Member.Supported_Section.Designation": inputs.get("beam_section") or inputs.get("supported_designation"), + "Member.Supported_Section.Material": inputs.get("supported_material"), + "Member.Supporting_Section.Designation": inputs.get("column_section") or inputs.get("supporting_designation") or inputs.get("primary_beam") or inputs.get("secondary_beam"), + "Member.Supporting_Section.Material": inputs.get("supporting_material"), + "Weld.Fab": inputs.get("weld_fab"), + "Weld.Material_Grade_OverWrite": inputs.get("weld_material_grade"), + "Connector.Plate.Thickness_List": inputs.get("plate_thickness") or [], + }) + + # Build YAML string manually to avoid new dependencies + lines: list[str] = [] + for key, value in legacy.items(): + if value is None: + continue + if isinstance(value, list): + if len(value) == 0: + # skip empty lists to avoid saving default/blank arrays + continue + lines.append(f"{key}:") + for item in value: + lines.append(f"- {qt(item)}") + else: + # skip empty strings + if isinstance(value, str) and value.strip() == "": + continue + lines.append(f"{key}: {value}") + + return "\n".join(lines) + "\n" + + +def serialize_osi(payload: Dict[str, Any]) -> str: + module_id = payload.get("module_id") or "" + name = payload.get("name") or "project" + inputs = payload.get("inputs") or {} + + # Use legacy YAML for known modules; else JSON for safety + if module_id in {"FinPlateConnection"}: + return _to_legacy_yaml(module_id, name, inputs) + + # default JSON + return json.dumps(payload, ensure_ascii=False, sort_keys=True, indent=2) + "\n" + + +def _parse_legacy_yaml(text: str) -> Dict[str, Any]: + """Very small YAML reader for the flat legacy format we emit. + + Supports lines like: + Key: value\n + Key:\n + - 'item'\n + Returns a dict of key -> value (str or list[str]). + """ + legacy: Dict[str, Any] = {} + lines = [ln.rstrip() for ln in text.splitlines() if ln.strip() != ""] + i = 0 + while i < len(lines): + line = lines[i] + if ":" in line: + key, after = line.split(":", 1) + key = key.strip() + after = after.strip() + if after == "": + # list block + i += 1 + items: list[str] = [] + while i < len(lines) and lines[i].lstrip().startswith("-"): + item = lines[i].lstrip()[1:].strip() + # strip optional quotes + if (item.startswith("'") and item.endswith("'")) or (item.startswith('"') and item.endswith('"')): + item = item[1:-1] + items.append(item) + i += 1 + legacy[key] = items + continue # already advanced i + else: + # scalar + val = after + # strip quotes + if (val.startswith("'") and val.endswith("'")) or (val.startswith('"') and val.endswith('"')): + val = val[1:-1] + legacy[key] = val + i += 1 + return legacy + + +def parse_osi(text: str) -> Tuple[str, str, Dict[str, Any]]: + # Try modern JSON first + try: + data = json.loads(text) + version = data.get("version") + if version not in SUPPORTED_VERSIONS: + raise ValueError("Unsupported OSI version") + name = data.get("name") + module_id = data.get("module_id") + inputs = data.get("inputs") + if not name or not validate_module_id(module_id) or not isinstance(inputs, dict): + raise ValueError("Malformed OSI payload") + return module_id, name, inputs + except Exception: + # Fallback to legacy flat YAML + legacy = _parse_legacy_yaml(text) + module_id = legacy.get("Module") or legacy.get("module") or "" + if not module_id: + raise ValueError("Module missing in OSI") + + def g(key: str, default: Any = None) -> Any: + return legacy.get(key, default) + + inputs: Dict[str, Any] = {} + if module_id == "FinPlateConnection": + # Reverse mapping for FinPlateConnection + inputs.update({ + "bolt_hole_type": g("Bolt.Bolt_Hole_Type"), + "bolt_diameter": g("Bolt.Diameter", []), + "bolt_grade": g("Bolt.Grade", []), + "bolt_slip_factor": g("Bolt.Slip_Factor"), + "bolt_tension_type": g("Bolt.TensionType"), + "bolt_type": (g("Bolt.Type") or "").replace(" ", "_"), + "connectivity": g("Connectivity"), + "connector_material": g("Connector.Material"), + "design_method": g("Design.Design_Method"), + "detailing_corr_status": g("Detailing.Corrosive_Influences"), + "detailing_edge_type": g("Detailing.Edge_type"), + "detailing_gap": g("Detailing.Gap"), + "load_axial": g("Load.Axial"), + "load_shear": g("Load.Shear"), + "material": g("Material"), + "beam_section": g("Member.Supported_Section.Designation"), + "supported_material": g("Member.Supported_Section.Material"), + "column_section": g("Member.Supporting_Section.Designation"), + "supporting_material": g("Member.Supporting_Section.Material"), + "weld_fab": g("Weld.Fab"), + "weld_material_grade": g("Weld.Material_Grade_OverWrite"), + "plate_thickness": g("Connector.Plate.Thickness_List", []), + "module": module_id, + }) + + name = legacy.get("name") or module_id + return module_id, name, inputs + + +def make_osifile_contentfile(payload: Dict[str, Any]) -> ContentFile: + content = serialize_osi(payload) + return ContentFile(content.encode("utf-8")) + + diff --git a/backend/apps/core/utils/report_image_generator.py b/backend/apps/core/utils/report_image_generator.py new file mode 100644 index 000000000..b67d87f48 --- /dev/null +++ b/backend/apps/core/utils/report_image_generator.py @@ -0,0 +1,997 @@ +""" +CAD Image Generation for Web Reports + +This module provides headless rendering of CAD files (BREP/STL) to PNG images +for inclusion in design reports. Supports multiple rendering backends with +automatic fallback chain. +""" + +import os +import sys +import logging +import shutil +from pathlib import Path +from typing import Dict, Optional, Tuple, Any + +logger = logging.getLogger(__name__) + + +def verify_vtk_available() -> bool: + """Verify VTK is installed and can be used for rendering. + + Returns: + True if VTK is available and working, False otherwise. + """ + try: + import vtk + logger.info(f"[SETUP] VTK available, version: {vtk.VTK_VERSION}") + return True + except ImportError as e: + logger.warning(f"[SETUP] VTK not available: {e}") + return False + except Exception as e: + logger.warning(f"[SETUP] VTK available but initialization failed: {e}") + return False + + +def verify_open3d_available() -> bool: + """Verify Open3D is installed and can create OffscreenRenderer (OPTIONAL). + + Returns: + True if Open3D is available and working, False otherwise. + """ + try: + import open3d as o3d + # Test creating renderer + renderer = o3d.visualization.rendering.OffscreenRenderer(100, 100, headless=True) + logger.info("[SETUP] Open3D available (optional), OffscreenRenderer works") + return True + except ImportError: + # Open3D is optional, don't warn + return False + except Exception as e: + logger.debug(f"[SETUP] Open3D available but renderer creation failed: {e}") + return False + + +def setup_headless_qt() -> bool: + """Setup environment for headless Qt rendering. + + Configures Qt for offscreen rendering. Xvfb is preferred but not strictly required + if Qt's offscreen platform plugin is available. + + Returns: + True if setup successful, False otherwise. + """ + try: + import subprocess + + # Check if Xvfb is available (preferred but not required) + result = subprocess.run(['which', 'Xvfb'], capture_output=True, text=True) + xvfb_available = result.returncode == 0 + + # Set Qt platform to offscreen (this is what matters for Qt rendering) + os.environ['QT_QPA_PLATFORM'] = 'offscreen' + + # Set display if Xvfb is available (for compatibility) + if xvfb_available: + if 'DISPLAY' not in os.environ: + os.environ['DISPLAY'] = ':99' + logger.info("[SETUP] Xvfb found, setting up headless Qt with Xvfb...") + else: + # Try without Xvfb - Qt's offscreen plugin might work + logger.warning("[SETUP] Xvfb not found, but attempting Qt offscreen rendering anyway...") + logger.info("[SETUP] Note: If Qt rendering fails, install Xvfb: sudo apt-get install xvfb") + + logger.info(f"[SETUP] QT_QPA_PLATFORM=offscreen") + logger.info(f"[SETUP] DISPLAY={os.environ.get('DISPLAY', 'not set')}") + return True + except Exception as e: + logger.warning(f"[SETUP] Failed to setup headless Qt: {e}") + return False + + +def read_brep_file(brep_path: str): + """Read BREP file and return TopoDS_Shape. + + Args: + brep_path: Path to BREP file + + Returns: + TopoDS_Shape object + + Raises: + FileNotFoundError: If BREP file doesn't exist + ValueError: If BREP file is invalid or can't be read + """ + from OCC.Core.BRepTools import BRepTools + from OCC.Core.BRep import BRep_Builder + from OCC.Core.TopoDS import TopoDS_Shape + + logger.info(f"[REPORT_IMG] Reading BREP: {brep_path}") + + if not os.path.exists(brep_path): + logger.error(f"[REPORT_IMG] BREP file not found: {brep_path}") + raise FileNotFoundError(f"BREP file not found: {brep_path}") + + file_size = os.path.getsize(brep_path) + logger.info(f"[REPORT_IMG] BREP file size: {file_size} bytes") + + try: + shape = TopoDS_Shape() + builder = BRep_Builder() + result = BRepTools.Read(shape, brep_path, builder) + + if not result: + raise ValueError(f"Failed to read BREP file: {brep_path}") + + logger.info("[REPORT_IMG] Successfully loaded shape") + return shape + except Exception as e: + logger.error(f"[REPORT_IMG] ERROR: Failed to read BREP - {e}") + raise ValueError(f"Failed to read BREP file: {e}") from e + + +def generate_stl_from_brep(brep_path: str, stl_path: str) -> str: + """Generate STL file from BREP using existing mesh_export utility. + + Args: + brep_path: Path to BREP file + stl_path: Path where STL should be saved + + Returns: + Path to generated STL file + + Raises: + Exception: If STL generation fails + """ + from apps.core.utils.mesh_export import write_stl + + logger.info(f"[REPORT_IMG] Generating STL from BREP: {brep_path} -> {stl_path}") + + shape = read_brep_file(brep_path) + write_stl(shape, stl_path) + + if not os.path.exists(stl_path): + raise RuntimeError(f"STL file was not created: {stl_path}") + + logger.info(f"[REPORT_IMG] STL file generated: {stl_path}") + return stl_path + + +def generate_with_open3d(brep_path: str, output_dir: str, module=None) -> Dict[str, str]: + """Generate report images using Open3D OffscreenRenderer (OPTIONAL, lightweight). + + This is an OPTIONAL fallback - Open3D is not in requirements.txt (~500MB). + Only used if Qt and VTK both fail. + NOTE: Open3D rendering does not support colors (single mesh only). + + Args: + brep_path: Path to BREP file + output_dir: Directory to save PNG images + module: Design module instance (not used for Open3D, but kept for consistency) + + Returns: + Dict mapping view names to image paths: {"3d": "path", "front": "path", ...} + + Raises: + Exception: If rendering fails + """ + import open3d as o3d + import numpy as np + + logger.info("[REPORT_IMG] Using Open3D OffscreenRenderer (optional fallback - no colors)...") + logger.warning("[REPORT_IMG] Open3D rendering produces single-color images (no part colors)") + + # Try STL first (if exists, faster and simpler) + stl_path = brep_path.replace('.brep', '.stl') + if os.path.exists(stl_path): + logger.info(f"[REPORT_IMG] Loading mesh from STL: {stl_path}") + mesh = o3d.io.read_triangle_mesh(stl_path) + if len(mesh.vertices) == 0: + raise ValueError(f"STL file is empty or invalid: {stl_path}") + else: + # Generate STL from BREP if it doesn't exist + logger.info(f"[REPORT_IMG] STL not found, generating from BREP...") + try: + generate_stl_from_brep(brep_path, stl_path) + mesh = o3d.io.read_triangle_mesh(stl_path) + if len(mesh.vertices) == 0: + raise ValueError(f"Generated STL file is empty: {stl_path}") + except Exception as e: + logger.error(f"[REPORT_IMG] Failed to generate/load STL: {e}") + raise + + logger.info(f"[REPORT_IMG] Mesh loaded: {len(mesh.vertices)} vertices, {len(mesh.triangles)} faces") + + # Create renderer + width, height = 800, 600 + logger.info(f"[REPORT_IMG] Creating OffscreenRenderer ({width}x{height}, headless=True)...") + renderer = o3d.visualization.rendering.OffscreenRenderer(width, height, headless=True) + + # Setup scene + renderer.scene.clear_geometry() + renderer.scene.add_geometry("mesh", mesh) + + # Setup material + mtl = renderer.scene.get_material("mesh") + mtl.base_color = [0.7, 0.7, 0.7, 1.0] # Light gray + mtl.shader = "defaultLit" + renderer.scene.update_material("mesh", mtl) + + # Setup lighting + renderer.scene.scene.enable_sun_light(True) + renderer.scene.scene.set_sun_light_direction([0.577, -0.577, -0.577]) + + # Get bounding box for camera positioning + bounds = mesh.get_axis_aligned_bounding_box() + center = bounds.get_center() + extent = bounds.get_extent() + max_extent = max(extent) if max(extent) > 0 else 1.0 + + # Define views: (camera_direction, up_direction) + views = { + '3d': { + 'camera_dir': np.array([1.0, 1.0, 1.0]), + 'up': np.array([0.0, 0.0, 1.0]), + 'distance': max_extent * 2.5 + }, + 'front': { + 'camera_dir': np.array([0.0, 0.0, 1.0]), + 'up': np.array([0.0, 1.0, 0.0]), + 'distance': max_extent * 2.5 + }, + 'top': { + 'camera_dir': np.array([0.0, 0.0, -1.0]), + 'up': np.array([0.0, 1.0, 0.0]), + 'distance': max_extent * 2.5 + }, + 'side': { + 'camera_dir': np.array([1.0, 0.0, 0.0]), + 'up': np.array([0.0, 0.0, 1.0]), + 'distance': max_extent * 2.5 + } + } + + image_paths = {} + + for view_name, view_config in views.items(): + logger.info(f"[REPORT_IMG] Rendering {view_name} view...") + + # Calculate camera position + camera_dir_normalized = view_config['camera_dir'] / np.linalg.norm(view_config['camera_dir']) + camera_pos = center + camera_dir_normalized * view_config['distance'] + + # Setup camera + camera = renderer.scene.get_camera() + + # Set projection + fov_deg = 60.0 + aspect = width / height + near = 0.1 + far = max_extent * 10.0 + camera.set_projection(fov_deg, aspect, near, far, o3d.camera.PinholeCameraIntrinsicParameters.Perspective) + + # Calculate look-at matrix + forward = center - camera_pos + forward_norm = np.linalg.norm(forward) + if forward_norm > 1e-6: + forward = forward / forward_norm + else: + forward = np.array([0.0, 0.0, -1.0]) + + right = np.cross(forward, view_config['up']) + right_norm = np.linalg.norm(right) + if right_norm > 1e-6: + right = right / right_norm + else: + right = np.array([1.0, 0.0, 0.0]) + + up_calc = np.cross(right, forward) + + # Build view matrix + view_matrix = np.eye(4) + view_matrix[0:3, 0] = right + view_matrix[0:3, 1] = up_calc + view_matrix[0:3, 2] = -forward + view_matrix[0:3, 3] = camera_pos + + camera.set_model_matrix(view_matrix) + renderer.scene.set_camera(camera) + + # Render + image = renderer.render_to_image() + + # Save PNG + output_path = os.path.join(output_dir, f'{view_name}.png') + o3d.io.write_image(output_path, image) + image_paths[view_name] = output_path + logger.info(f"[REPORT_IMG] Image saved to: {output_path}") + + logger.info("[REPORT_IMG] Open3D rendering completed successfully") + return image_paths + + +def generate_with_vtk(brep_path: str, output_dir: str, module=None) -> Dict[str, str]: + """Generate report images using VTK (headless rendering, already installed). + + This is the FALLBACK method - uses VTK which is already in requirements.txt. + Pure headless, no Xvfb needed. + NOTE: VTK rendering does not support colors (single mesh only). + + Args: + brep_path: Path to BREP file + output_dir: Directory to save PNG images + module: Design module instance (not used for VTK, but kept for consistency) + + Returns: + Dict mapping view names to image paths: {"3d": "path", "front": "path", ...} + + Raises: + Exception: If rendering fails + """ + import vtk + import numpy as np + + logger.info("[REPORT_IMG] Using VTK headless rendering (fallback method - no colors)...") + logger.warning("[REPORT_IMG] VTK rendering produces single-color images (no part colors)") + + # Try STL first (if exists, faster) + stl_path = brep_path.replace('.brep', '.stl') + if not os.path.exists(stl_path): + # Generate STL from BREP if it doesn't exist + logger.info(f"[REPORT_IMG] STL not found, generating from BREP...") + try: + generate_stl_from_brep(brep_path, stl_path) + except Exception as e: + logger.error(f"[REPORT_IMG] Failed to generate STL: {e}") + raise + + # Read STL file + logger.info(f"[REPORT_IMG] Reading STL file: {stl_path}") + reader = vtk.vtkSTLReader() + reader.SetFileName(stl_path) + reader.Update() + + # Create mapper and actor + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputConnection(reader.GetOutputPort()) + + actor = vtk.vtkActor() + actor.SetMapper(mapper) + actor.GetProperty().SetColor(0.7, 0.7, 0.7) # Light gray + actor.GetProperty().SetSpecular(0.3) + actor.GetProperty().SetSpecularPower(30.0) + + # Create renderer + renderer = vtk.vtkRenderer() + renderer.AddActor(actor) + renderer.SetBackground(1.0, 1.0, 1.0) # White background + + # Add lighting + light1 = vtk.vtkLight() + light1.SetPosition(1.0, 1.0, 1.0) + light1.SetIntensity(0.8) + renderer.AddLight(light1) + + light2 = vtk.vtkLight() + light2.SetPosition(-1.0, -1.0, -1.0) + light2.SetIntensity(0.4) + renderer.AddLight(light2) + + # Get bounding box for camera positioning + bounds = actor.GetBounds() + center = [ + (bounds[0] + bounds[1]) / 2.0, + (bounds[2] + bounds[3]) / 2.0, + (bounds[4] + bounds[5]) / 2.0 + ] + extent = [ + bounds[1] - bounds[0], + bounds[3] - bounds[2], + bounds[5] - bounds[4] + ] + max_extent = max(extent) if max(extent) > 0 else 1.0 + + # Define views: (camera_position_offset, focal_point_offset, view_up) + views = { + '3d': { + 'camera_pos': [1.0, 1.0, 1.0], + 'focal_point': [0.0, 0.0, 0.0], + 'view_up': [0.0, 0.0, 1.0] + }, + 'front': { + 'camera_pos': [0.0, 0.0, 1.0], + 'focal_point': [0.0, 0.0, 0.0], + 'view_up': [0.0, 1.0, 0.0] + }, + 'top': { + 'camera_pos': [0.0, 0.0, -1.0], + 'focal_point': [0.0, 0.0, 0.0], + 'view_up': [0.0, 1.0, 0.0] + }, + 'side': { + 'camera_pos': [1.0, 0.0, 0.0], + 'focal_point': [0.0, 0.0, 0.0], + 'view_up': [0.0, 0.0, 1.0] + } + } + + image_paths = {} + width, height = 800, 600 + + # Create render window once (reuse for all views) + render_window = vtk.vtkRenderWindow() + render_window.SetOffScreenRendering(1) + render_window.SetSize(width, height) + render_window.AddRenderer(renderer) + + for view_name, view_config in views.items(): + logger.info(f"[REPORT_IMG] Rendering {view_name} view...") + + # Calculate camera position relative to center + camera_dir = np.array(view_config['camera_pos']) + camera_dir_normalized = camera_dir / np.linalg.norm(camera_dir) + camera_pos = np.array(center) + camera_dir_normalized * max_extent * 2.5 + + # Reset and setup camera for this view + camera = renderer.GetActiveCamera() + camera.SetPosition(camera_pos[0], camera_pos[1], camera_pos[2]) + camera.SetFocalPoint(center[0], center[1], center[2]) + camera.SetViewUp(view_config['view_up'][0], view_config['view_up'][1], view_config['view_up'][2]) + camera.OrthogonalizeViewUp() + + # Reset camera to fit bounds, then adjust for view + renderer.ResetCamera(bounds) + camera = renderer.GetActiveCamera() + + # Adjust camera distance for better view + camera.Dolly(0.7) # Zoom out a bit + camera.SetViewAngle(60.0) + + # Ensure proper clipping range + camera.SetClippingRange(0.1, max_extent * 10.0) + + # Render the scene + render_window.Render() + + # Export to image + window_to_image = vtk.vtkWindowToImageFilter() + window_to_image.SetInput(render_window) + window_to_image.SetInputBufferTypeToRGB() + window_to_image.ReadFrontBufferOff() + window_to_image.Update() + + # Write PNG + writer = vtk.vtkPNGWriter() + output_path = os.path.join(output_dir, f'{view_name}.png') + writer.SetFileName(output_path) + writer.SetInputConnection(window_to_image.GetOutputPort()) + writer.Write() + + image_paths[view_name] = output_path + logger.info(f"[REPORT_IMG] Image saved to: {output_path}") + + # Clean up filter for next iteration + window_to_image = None + + logger.info("[REPORT_IMG] VTK rendering completed successfully") + return image_paths + + +def generate_with_opencascade_display(brep_path: str, output_dir: str, module=None) -> Dict[str, str]: + """Generate report images using OpenCASCADE display (EXACT replica of osdag_gui WITH COLORS). + + This is the PRIMARY method - requires Qt and Xvfb, but produces exact GUI replica with colored parts. + + Args: + brep_path: Path to BREP file (for reference, but we use module CAD generation instead) + output_dir: Directory to save PNG images + module: Design module instance (required for colored rendering) + + Returns: + Dict mapping view names to image paths: {"3d": "path", "front": "path", ...} + + Raises: + Exception: If rendering fails + """ + # CRITICAL: Set QT_QPA_PLATFORM=offscreen BEFORE any Qt imports + # This must happen before init_display() is called, as Qt reads this env var on first import + os.environ['QT_QPA_PLATFORM'] = 'offscreen' + + logger.info("[REPORT_IMG] ========================================") + logger.info("[REPORT_IMG] ATTEMPTING Qt/OpenCASCADE RENDERING") + logger.info("[REPORT_IMG] This should produce COLORS like GUI") + logger.info("[REPORT_IMG] ========================================") + + # Setup headless Qt (will try even without Xvfb) + setup_headless_qt() # This also sets QT_QPA_PLATFORM, but we already set it above + + # Try to create Qt display - this will fail if Qt offscreen doesn't work + # Create display (same as GUI) + # Use 'pyside6' backend (PySide6 is already installed) - QT_QPA_PLATFORM=offscreen makes it headless + try: + from osdag_core.texlive.Design_wrapper import init_display as init_display_off_screen + off_display, _, _, _ = init_display_off_screen(backend_str='pyside6') + logger.info("[REPORT_IMG] Off-screen display created successfully") + except Exception as e: + error_msg = str(e) + logger.error(f"[REPORT_IMG] Failed to create Qt offscreen display: {error_msg}") + if "offscreen" in error_msg.lower() or "display" in error_msg.lower() or "platform plugin" in error_msg.lower(): + logger.error("[REPORT_IMG] Qt offscreen rendering requires proper Qt platform plugin setup.") + logger.error("[REPORT_IMG] Install missing dependencies: sudo apt-get install libxcb-cursor0") + logger.error("[REPORT_IMG] Or use Xvfb: sudo apt-get install xvfb && Xvfb :99 -screen 0 1024x768x24 &") + raise RuntimeError(f"Cannot create Qt offscreen display: {e}. Install required Qt dependencies or Xvfb for headless rendering.") from e + + # Use module CAD generation for colored rendering (like GUI) + if module is not None: + logger.info("[REPORT_IMG] Using module CAD generation for colored rendering (GUI approach)...") + try: + # Create CommonDesignLogic object (like GUI does) + from osdag_core.cad.common_logic import CommonDesignLogic + + # Create a minimal mock cad_widget (headless - no GUI needed) + class MockCADWidget: + def __init__(self): + self.model_ais_objects = {} + self.model_hover_labels = {} + def display_view_cube(self): + pass # No-op for headless + + mock_widget = MockCADWidget() + + # Get module attributes needed for CAD generation + mainmodule = getattr(module, 'mainmodule', 'Shear Connection') + connection = getattr(module, 'module', 'FinPlateConnection') + folder = None # Not needed for headless + + logger.info(f"[REPORT_IMG] Module attributes:") + logger.info(f"[REPORT_IMG] - mainmodule: {mainmodule}") + logger.info(f"[REPORT_IMG] - connection: {connection}") + logger.info(f"[REPORT_IMG] - design_status: {getattr(module, 'design_status', 'NOT SET')}") + logger.info(f"[REPORT_IMG] - has hover_dict: {hasattr(module, 'hover_dict')}") + logger.info(f"[REPORT_IMG] Creating CommonDesignLogic...") + + # Create CommonDesignLogic + comm_logic = CommonDesignLogic( + display=off_display, + cad_widget=mock_widget, + folder=folder, + connection=connection, + mainmodule=mainmodule + ) + + # Call call_3DModel first (like GUI does) - this creates CAD objects with parts + # flag=True means design exists, module_object is the module instance + logger.info("[REPORT_IMG] Calling call_3DModel(True, module)...") + design_status = getattr(module, 'design_status', True) + comm_logic.call_3DModel(design_status, module) + logger.info("[REPORT_IMG] CAD model created via call_3DModel (with colored parts)") + + # Note: call_3DModel already calls display_3DModel("Model", "gradient_bg") internally + # which displays all parts with colors. We just need to change background to white for reports. + # Don't call display_3DModel again - it would erase everything via cleanup coordinator! + logger.info("[REPORT_IMG] Changing background to white for report images...") + off_display.set_bg_gradient_color([255, 255, 255], [126, 126, 126]) + + # Verify connectivityObj was created (contains the colored CAD parts) + if not hasattr(comm_logic, 'connectivityObj') or comm_logic.connectivityObj is None: + logger.error("[REPORT_IMG] ERROR: connectivityObj is None - CAD model was not created!") + raise RuntimeError("connectivityObj is None - CAD generation failed") + logger.info("[REPORT_IMG] Verified: connectivityObj exists (CAD model created successfully)") + + # CRITICAL: Force display update to ensure colors are rendered before export + logger.info("[REPORT_IMG] Forcing display update to ensure colors are rendered...") + off_display.Repaint() # Repaint() is the correct method for OpenCASCADE display + # Small delay to ensure rendering completes (headless rendering can be async) + import time + time.sleep(0.1) + logger.info("[REPORT_IMG] Model displayed with colors and white background (ready for export)") + + except Exception as e: + logger.error(f"[REPORT_IMG] Failed to use module CAD generation: {e}") + import traceback + logger.error(traceback.format_exc()) + raise RuntimeError(f"Module CAD generation failed: {e}") from e + else: + # Fallback: Just load BREP (no colors) - COMMENTED OUT, prefer module approach + logger.warning("[REPORT_IMG] No module provided, cannot generate colored images") + raise RuntimeError("Module is required for colored rendering. BREP-only rendering disabled.") + + # OLD APPROACH (COMMENTED OUT - no colors): + # shape = read_brep_file(brep_path) + # off_display.DisplayShape(shape, update=True) + # off_display.set_bg_gradient_color([255, 255, 255], [255, 255, 255]) + + # Export images (EXACT same sequence as GUI) + image_paths = {} + + logger.info("[REPORT_IMG] Exporting 3D view...") + off_display.Repaint() # Ensure display is updated before export + off_display.ExportToImage(os.path.join(output_dir, '3d.png')) + image_paths['3d'] = os.path.join(output_dir, '3d.png') + + logger.info("[REPORT_IMG] Exporting Front view...") + off_display.View_Front() + off_display.FitAll() + off_display.Repaint() # Ensure display is updated before export + off_display.ExportToImage(os.path.join(output_dir, 'front.png')) + image_paths['front'] = os.path.join(output_dir, 'front.png') + + logger.info("[REPORT_IMG] Exporting Top view...") + off_display.View_Top() + off_display.FitAll() + off_display.Repaint() # Ensure display is updated before export + off_display.ExportToImage(os.path.join(output_dir, 'top.png')) + image_paths['top'] = os.path.join(output_dir, 'top.png') + + logger.info("[REPORT_IMG] Exporting Side view...") + off_display.View_Right() + off_display.FitAll() + off_display.Repaint() # Ensure display is updated before export + off_display.ExportToImage(os.path.join(output_dir, 'side.png')) + image_paths['side'] = os.path.join(output_dir, 'side.png') + + logger.info("[REPORT_IMG] OpenCASCADE rendering completed successfully") + return image_paths + + +def find_existing_cad_files(module_id: str, input_values: dict, + session_id: Optional[str] = None) -> Dict[str, str]: + """Find existing CAD files for a design. + + Searches in file_storage/cad_models/ for BREP or STL files matching + the session_id or module_id pattern. + + Args: + module_id: Module identifier (e.g., 'FinPlateConnection') + input_values: Design input values (for future use) + session_id: Optional session ID to search for + + Returns: + Dict with section -> file_path mapping: {"Model": "path/to/Model.brep", ...} + """ + logger.info("[REPORT_CAD] Searching for CAD files...") + + cad_models_dir = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_dir): + logger.info("[REPORT_CAD] CAD models directory does not exist") + return {} + + found_files = {} + + # If session_id provided, search for files with that session_id + if session_id: + logger.info(f"[REPORT_CAD] Session ID: {session_id}") + pattern = f"{session_id}_*.brep" + stl_pattern = f"{session_id}_*.stl" + + for file in os.listdir(cad_models_dir): + if file.startswith(f"{session_id}_") and (file.endswith('.brep') or file.endswith('.stl')): + section = file.replace(f"{session_id}_", "").replace('.brep', '').replace('.stl', '') + file_path = os.path.join(cad_models_dir, file) + found_files[section] = file_path + logger.info(f"[REPORT_CAD] Found CAD file: {section} -> {file_path}") + else: + # Search by module_id pattern (less reliable, but fallback) + logger.info(f"[REPORT_CAD] No session_id, searching by module pattern...") + # This is a simplified search - in practice, you might want more sophisticated matching + for file in os.listdir(cad_models_dir): + if file.endswith('.brep') or file.endswith('.stl'): + # Try to match by checking if file might be related + # This is a placeholder - actual implementation might need better logic + file_path = os.path.join(cad_models_dir, file) + section = file.replace('.brep', '').replace('.stl', '').split('_')[-1] + if section == 'Model': # Only take Model section for now + found_files[section] = file_path + logger.info(f"[REPORT_CAD] Found CAD file: {section} -> {file_path}") + + if found_files: + logger.info(f"[REPORT_CAD] Found CAD files: {list(found_files.keys())}") + else: + logger.info("[REPORT_CAD] No existing CAD files found, will generate") + + return found_files + + +def generate_cad_files_for_report(module: Any, input_values: dict, + module_id: str, session_id: Optional[str] = None) -> Dict[str, str]: + """Generate CAD files for report if they don't exist. + + Reuses CAD generation logic from module adapters. + Generates CAD files on-the-fly if they don't exist. + + Args: + module: Design module instance + input_values: Design input values + module_id: Module identifier + session_id: Optional session ID for file naming + + Returns: + Dict of section -> file_path: {"Model": "path/to/Model.brep", ...} + """ + logger.info("[REPORT_CAD] Generating CAD files on-the-fly...") + + if session_id is None: + import uuid + session_id = str(uuid.uuid4()) + logger.info(f"[REPORT_CAD] Generated session_id: {session_id}") + + # Map module_id to adapter function + module_adapter_map = { + 'FinPlateConnection': 'apps.modules.shear_connection.submodules.fin_plate.adapter', + 'EndPlateConnection': 'apps.modules.shear_connection.submodules.end_plate.adapter', + 'CleatAngleConnection': 'apps.modules.shear_connection.submodules.cleat_angle.adapter', + 'Seated-Angle-Connection': 'apps.modules.shear_connection.submodules.seated_angle.adapter', + 'Cover-Plate-Bolted-Connection': 'apps.modules.moment_connection.submodules.beam_beam_cover_plate_bolted.adapter', + 'Beam-Beam-End-Plate-Connection': 'apps.modules.moment_connection.submodules.beam_beam_end_plate.adapter', + 'Cover-Plate-Welded-Connection': 'apps.modules.moment_connection.submodules.beam_beam_cover_plate_welded.adapter', + 'Beam-to-Column-End-Plate-Connection': 'apps.modules.moment_connection.submodules.beam_column_end_plate.adapter', + 'ColumnCoverPlateBolted': 'apps.modules.moment_connection.submodules.column_column_cover_plate_bolted.adapter', + 'Column-to-Column-Cover-Plate-Welded-Connection': 'apps.modules.moment_connection.submodules.column_column_cover_plate_welded.adapter', + 'Column-to-Column-End-Plate-Connection': 'apps.modules.moment_connection.submodules.column_column_end_plate.adapter', + } + + if module_id not in module_adapter_map: + logger.warning(f"[REPORT_CAD] Module {module_id} not in adapter map, cannot generate CAD") + return {} + + try: + # Import the adapter module + adapter_module_path = module_adapter_map[module_id] + adapter_module = __import__(adapter_module_path, fromlist=['create_cad_model']) + create_cad_model_func = getattr(adapter_module, 'create_cad_model', None) + + if not create_cad_model_func: + logger.warning(f"[REPORT_CAD] create_cad_model not found in {adapter_module_path}") + return {} + + # Generate CAD for Model section + logger.info(f"[REPORT_CAD] Calling create_cad_model for section: Model") + cad_path = create_cad_model_func(input_values, "Model", session_id) + + if not cad_path: + logger.warning(f"[REPORT_CAD] create_cad_model returned None or empty") + return {} + + # Resolve full path + if not os.path.isabs(cad_path): + cad_path = os.path.join(os.getcwd(), cad_path) + + if os.path.exists(cad_path): + logger.info(f"[REPORT_CAD] CAD file generated: {cad_path}") + return {"Model": cad_path} + else: + logger.warning(f"[REPORT_CAD] Generated CAD file does not exist: {cad_path}") + return {} + + except Exception as e: + logger.error(f"[REPORT_CAD] ERROR: CAD generation failed - {e}") + import traceback + logger.error(traceback.format_exc()) + return {} + + +def ensure_image_directory(image_dir: str) -> str: + """Ensure image directory exists and return absolute path. + + Args: + image_dir: Directory path (relative or absolute) + + Returns: + Absolute path to image directory + """ + if not os.path.isabs(image_dir): + image_dir = os.path.join(os.getcwd(), image_dir) + + logger.info(f"[REPORT_IMG] Creating image directory: {image_dir}") + + if not os.path.exists(image_dir): + os.makedirs(image_dir, exist_ok=True) + logger.info(f"[REPORT_IMG] Image directory created: {image_dir}") + else: + logger.info(f"[REPORT_IMG] Image directory exists: {image_dir}") + + return image_dir + + +def copy_fallback_images(output_dir: str) -> Dict[str, str]: + """Copy broken.png as fallback for all views. + + Args: + output_dir: Directory to save fallback images + + Returns: + Dict mapping view names to fallback image paths + """ + logger.info("[REPORT_IMG] WARNING: Image generation failed, using fallback") + + # Find broken.png in osdag_core data + from osdag_core.Common import _get_resource_path + pkg_images = _get_resource_path("data", "ResourceFiles", "images") + broken_png_path = pkg_images / "broken.png" + + if not os.path.exists(str(broken_png_path)): + logger.error(f"[REPORT_IMG] Fallback image not found: {broken_png_path}") + return {} + + image_paths = {} + for view_name in ['3d', 'front', 'top', 'side']: + dest_path = os.path.join(output_dir, f'{view_name}.png') + shutil.copy2(str(broken_png_path), dest_path) + image_paths[view_name] = dest_path + logger.info(f"[REPORT_IMG] Copied fallback image: {dest_path}") + + logger.info("[REPORT_IMG] Fallback images set for all views") + return image_paths + + +def generate_cad_images_for_report( + module: Any, + input_values: dict, + module_id: str, + report_dir: str, + session_id: Optional[str] = None +) -> Dict[str, Any]: + """Main function to generate CAD images for report. + + This function orchestrates the entire image generation pipeline: + 1. Find or generate CAD files + 2. Convert BREP/STL to images using rendering backend + 3. Copy images to ResourceFiles/images/ + 4. Return paths + + Args: + module: Design module instance + input_values: Design input values + module_id: Module identifier + report_dir: Base directory for report files + session_id: Optional session ID for CAD file discovery + + Returns: + Dict with: + - success: bool + - images: {"3d": "path", "front": "path", ...} + - errors: List of error messages + """ + logger.info("[REPORT_IMG] Starting image generation pipeline...") + + result = { + "success": False, + "images": {}, + "errors": [] + } + + try: + # Step 1: Check if module is available (required for colored rendering) + if module is None: + logger.warning("[REPORT_IMG] No module provided, cannot generate colored images") + result["errors"].append("Module is required for colored rendering") + image_dir = ensure_image_directory(os.path.join(report_dir, "ResourceFiles", "images")) + result["images"] = copy_fallback_images(image_dir) + return result + + # Step 2: Convert to images using module CAD generation (like GUI) + logger.info("[REPORT_IMG] Step 2/4: Converting to images using module CAD generation...") + image_dir = ensure_image_directory(os.path.join(report_dir, "ResourceFiles", "images")) + logger.info(f"[REPORT_IMG] Images will be saved to: {image_dir}") + logger.info(f"[REPORT_IMG] report_dir: {report_dir}") + + # For Qt rendering, we don't need CAD files - module generates CAD directly + # For VTK/Open3D fallbacks, we may need CAD files, so find them if needed + cad_file_path = None # Will be set only if needed for fallback + + # Try Qt first (PRIMARY - exact GUI replica WITH COLORS, already installed) + try: + logger.info("[REPORT_IMG] ===== USING Qt/OpenCASCADE RENDERING (WITH COLORS) =====") + logger.info("[REPORT_IMG] This is the PRIMARY method - exact GUI replica with colored parts") + # Qt rendering uses module directly, no CAD file needed + image_paths = generate_with_opencascade_display("", image_dir, module=module) + result["success"] = True + result["images"] = image_paths + logger.info(f"[REPORT_IMG] ===== Qt rendering SUCCESS - Generated colored images: {list(image_paths.keys())} =====") + except Exception as e: + logger.error(f"[REPORT_IMG] ===== Qt rendering FAILED: {e} =====") + logger.warning(f"[REPORT_IMG] Falling back to VTK (NO COLORS)...") + import traceback + logger.error(traceback.format_exc()) + result["errors"].append(f"Qt rendering failed: {e}") + + # For fallback methods, we need CAD files + logger.info("[REPORT_IMG] Finding CAD files for fallback rendering...") + cad_files = find_existing_cad_files(module_id, input_values, session_id) + + if not cad_files: + logger.info("[REPORT_IMG] No existing CAD files, generating on-the-fly...") + cad_files = generate_cad_files_for_report(module, input_values, module_id, session_id) + + if not cad_files: + logger.warning("[REPORT_IMG] No CAD files available for fallback, using broken.png") + result["images"] = copy_fallback_images(image_dir) + return result + + # Get Model section (primary CAD file) + model_file = cad_files.get("Model") + if not model_file: + logger.warning("[REPORT_IMG] No Model section in CAD files") + result["images"] = copy_fallback_images(image_dir) + return result + + # Determine file path (prefer STL, fallback to BREP) + brep_path = model_file + stl_path = model_file.replace('.brep', '.stl') + if os.path.exists(stl_path): + cad_file_path = stl_path + elif os.path.exists(brep_path): + cad_file_path = brep_path + else: + logger.error(f"[REPORT_IMG] CAD file not found: {model_file}") + result["images"] = copy_fallback_images(image_dir) + return result + + # Try VTK fallback (already installed, lighter) - NOTE: No colors + try: + if verify_vtk_available(): + logger.info("[REPORT_IMG] Using VTK (fallback method - already installed, no colors)...") + image_paths = generate_with_vtk(cad_file_path, image_dir, module=module) + result["success"] = True + result["images"] = image_paths + logger.info(f"[REPORT_IMG] Generated images: {list(image_paths.keys())}") + else: + raise RuntimeError("VTK not available") + except Exception as e2: + logger.warning(f"[REPORT_IMG] VTK rendering failed: {e2}, trying Open3D (optional)...") + result["errors"].append(f"VTK rendering failed: {e2}") + + # Try Open3D (OPTIONAL - not in requirements.txt) - NOTE: No colors + try: + if verify_open3d_available(): + logger.info("[REPORT_IMG] Using Open3D (optional fallback - not in requirements, no colors)...") + image_paths = generate_with_open3d(cad_file_path, image_dir, module=module) + result["success"] = True + result["images"] = image_paths + logger.info(f"[REPORT_IMG] Generated images: {list(image_paths.keys())}") + else: + raise RuntimeError("Open3D not available (optional package)") + except Exception as e3: + logger.error(f"[REPORT_IMG] All rendering methods failed: {e3}") + result["errors"].append(f"Open3D rendering failed: {e3}") + # Use fallback images + result["images"] = copy_fallback_images(image_dir) + + # Step 3: Verify images exist + logger.info("[REPORT_IMG] Step 3/4: Verifying images...") + verified_images = {} + for view_name, img_path in result["images"].items(): + if os.path.exists(img_path): + verified_images[view_name] = img_path + else: + logger.warning(f"[REPORT_IMG] Image file missing: {img_path}") + result["errors"].append(f"Image file missing: {img_path}") + + if not verified_images: + logger.warning("[REPORT_IMG] No images verified, using fallback") + result["images"] = copy_fallback_images(image_dir) + else: + result["images"] = verified_images + + # Step 4: Complete + logger.info("[REPORT_IMG] Step 4/4: Image generation complete") + logger.info(f"[REPORT_IMG] Generated images: {list(result['images'].keys())}") + + except Exception as e: + logger.error(f"[REPORT_IMG] ERROR: Image generation pipeline failed: {e}") + import traceback + logger.error(traceback.format_exc()) + result["errors"].append(f"Pipeline error: {e}") + + # Ensure fallback images are set + try: + image_dir = ensure_image_directory(os.path.join(report_dir, "ResourceFiles", "images")) + result["images"] = copy_fallback_images(image_dir) + except Exception as fallback_err: + logger.error(f"[REPORT_IMG] Even fallback failed: {fallback_err}") + result["errors"].append(f"Fallback failed: {fallback_err}") + + return result + diff --git a/backend/apps/core/utils/tests/__init__.py b/backend/apps/core/utils/tests/__init__.py new file mode 100644 index 000000000..7ceaf035a --- /dev/null +++ b/backend/apps/core/utils/tests/__init__.py @@ -0,0 +1,3 @@ +# Tests for utils module + + diff --git a/backend/apps/core/utils/tests/test_report_image_generator.py b/backend/apps/core/utils/tests/test_report_image_generator.py new file mode 100644 index 000000000..a05bedd22 --- /dev/null +++ b/backend/apps/core/utils/tests/test_report_image_generator.py @@ -0,0 +1,177 @@ +""" +Unit tests for report_image_generator module. +""" + +import os +import pytest +import tempfile +import shutil +from unittest.mock import Mock, patch, MagicMock +import numpy as np + +# Import the module under test +import sys +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../..')) +from apps.core.utils.report_image_generator import ( + verify_open3d_available, + setup_headless_qt, + read_brep_file, + generate_stl_from_brep, + find_existing_cad_files, + generate_cad_files_for_report, + ensure_image_directory, + copy_fallback_images, + generate_cad_images_for_report +) + + +class TestVerifyOpen3DAvailable: + """Tests for verify_open3d_available()""" + + def test_open3d_available(self): + """Test when Open3D is available""" + with patch('apps.core.utils.report_image_generator.open3d') as mock_o3d: + mock_renderer = MagicMock() + mock_o3d.visualization.rendering.OffscreenRenderer.return_value = mock_renderer + result = verify_open3d_available() + assert result is True + + def test_open3d_not_available(self): + """Test when Open3D is not installed""" + with patch.dict('sys.modules', {'open3d': None}): + with pytest.raises(ImportError): + import open3d + # Function should handle gracefully + # Note: This test may fail if open3d is actually installed + pass + + +class TestReadBrepFile: + """Tests for read_brep_file()""" + + def test_file_not_found(self): + """Test with non-existent file""" + with pytest.raises(FileNotFoundError): + read_brep_file("/nonexistent/file.brep") + + def test_invalid_brep(self, tmp_path): + """Test with invalid BREP file""" + invalid_brep = tmp_path / "invalid.brep" + invalid_brep.write_text("not a valid brep file") + + with pytest.raises(ValueError): + read_brep_file(str(invalid_brep)) + + +class TestFindExistingCadFiles: + """Tests for find_existing_cad_files()""" + + def test_no_files_found(self, tmp_path): + """Test when no CAD files exist""" + with patch('os.getcwd', return_value=str(tmp_path)): + result = find_existing_cad_files("TestModule", {}, None) + assert result == {} + + def test_files_found_with_session_id(self, tmp_path): + """Test finding files with session_id""" + cad_dir = tmp_path / "file_storage" / "cad_models" + cad_dir.mkdir(parents=True) + + session_id = "test_session_123" + test_file = cad_dir / f"{session_id}_Model.brep" + test_file.write_text("test brep content") + + with patch('os.getcwd', return_value=str(tmp_path)): + result = find_existing_cad_files("TestModule", {}, session_id) + assert "Model" in result + assert result["Model"] == str(test_file) + + +class TestEnsureImageDirectory: + """Tests for ensure_image_directory()""" + + def test_create_directory(self, tmp_path): + """Test directory creation""" + image_dir = tmp_path / "test_images" + result = ensure_image_directory(str(image_dir)) + + assert os.path.exists(result) + assert os.path.isabs(result) + + def test_existing_directory(self, tmp_path): + """Test with existing directory""" + image_dir = tmp_path / "existing_images" + image_dir.mkdir() + + result = ensure_image_directory(str(image_dir)) + assert os.path.exists(result) + + +class TestCopyFallbackImages: + """Tests for copy_fallback_images()""" + + def test_fallback_copy(self, tmp_path): + """Test copying fallback images""" + output_dir = tmp_path / "output" + output_dir.mkdir() + + # Mock broken.png location + with patch('apps.core.utils.report_image_generator._get_resource_path') as mock_get_path: + mock_broken = tmp_path / "broken.png" + mock_broken.write_bytes(b"fake png data") + mock_get_path.return_value = tmp_path + + result = copy_fallback_images(str(output_dir)) + + assert len(result) == 4 + assert '3d' in result + assert 'front' in result + assert 'top' in result + assert 'side' in result + + +class TestGenerateCadImagesForReport: + """Tests for generate_cad_images_for_report()""" + + def test_no_cad_files(self): + """Test when no CAD files are available""" + mock_module = Mock() + + with patch('apps.core.utils.report_image_generator.find_existing_cad_files', return_value={}): + with patch('apps.core.utils.report_image_generator.generate_cad_files_for_report', return_value={}): + with patch('apps.core.utils.report_image_generator.copy_fallback_images') as mock_fallback: + mock_fallback.return_value = {'3d': 'path', 'front': 'path', 'top': 'path', 'side': 'path'} + + result = generate_cad_images_for_report( + module=mock_module, + input_values={}, + module_id="TestModule", + report_dir="/tmp/test" + ) + + assert result["success"] is False + assert "images" in result + assert len(result["images"]) == 4 + + @pytest.mark.skipif(not verify_open3d_available(), reason="Open3D not available") + def test_with_cad_files_open3d(self, tmp_path): + """Test image generation with Open3D (if available)""" + # Create a minimal STL file for testing + cad_dir = tmp_path / "file_storage" / "cad_models" + cad_dir.mkdir(parents=True) + + # Note: This would require a real STL file or mock + # For now, just test the structure + pass + + +# Integration test placeholder +class TestIntegration: + """Integration tests - to be implemented""" + + @pytest.mark.skip(reason="Requires full environment setup") + def test_full_pipeline(self): + """Test full image generation pipeline""" + pass + + diff --git a/backend/apps/core/utils/validation.py b/backend/apps/core/utils/validation.py new file mode 100644 index 000000000..5e0105afe --- /dev/null +++ b/backend/apps/core/utils/validation.py @@ -0,0 +1,95 @@ +""" +Validation utilities for the backend. +Contains utility functions for input validation and type checking. +""" +from typing import List, Tuple, Callable, Any, Optional, Iterable +from .errors import InvalidInputTypeError + + +def validate_list_type(iterable: Iterable, data_type: Any) -> bool: + """Validate whether the all items of list are of data type.""" + if len(iterable) == 0: + return False + for item in iterable: + if not isinstance(item, data_type): + return False + return True + + +def contains_keys(data: dict, keys: List[str]) -> Optional[Tuple[str]]: + """Check whether dictionary contains all given keys.""" + missing = [] + for key in keys: + if key not in data.keys(): + missing.append(key) + if missing != []: + return tuple(missing) + + +def custom_list_validation(iterable: Iterable, validation: Callable[[Any], bool]) -> bool: + """Validate all items in the list using a custom function.""" + for item in iterable: + if not validation(item): + print(item) + return False + return True + + +def int_able(value: str) -> bool: + """Check if str can be converted to int.""" + try: + int(value) + except: + return False + return True + + +def float_able(value: str) -> bool: + """Check if str can be converted to float.""" + try: + float(value) + except: + return False + return True + + +def is_yes_or_no(value: Any) -> bool: + """Checks if value is 'Yes' or 'No'.""" + if not isinstance(value, str): + return False + if not (value == "Yes" or value == "No"): + return False + return True + + +def validate_string(key: str, value: Any) -> None: + """Check if value is a string. If not, raise error.""" + if not isinstance(value, str): + raise InvalidInputTypeError(key, "str") + + +def validate_num(key: str, value: Any, is_float: bool) -> None: + """Check if value is a string that can be converted to a number. If not, raise error.""" + if is_float: + checker = float_able + type_str = "float" + else: + checker = int_able + type_str = "int" + if (not isinstance(value, str) or not checker(value)): + raise InvalidInputTypeError(key, "str where str can be converted to " + type_str) + + +def validate_arr(key: str, value: Any, is_float: bool) -> None: + """Check if value is a list where all items can be converted to numbers.""" + if is_float: + checker = float_able + type_str = "float" + else: + checker = int_able + type_str = "int" + if (not isinstance(value, list) + or not validate_list_type(value, str) + or not custom_list_validation(value, checker)): + raise InvalidInputTypeError(key, "non empty List[str] where all items can be converted to " + type_str) + diff --git a/backend/apps/core/views.py b/backend/apps/core/views.py new file mode 100644 index 000000000..d1defbe97 --- /dev/null +++ b/backend/apps/core/views.py @@ -0,0 +1,208 @@ +from django.http.response import JsonResponse +from rest_framework.decorators import api_view, permission_classes, authentication_classes +from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from firebase_admin import auth as firebase_auth +import firebase_admin +from firebase_admin import credentials +from django.contrib.auth.models import User +import os + +# importing data +from .data.design_types import design_type_data, connections_data, shear_connection, moment_connection, b2b_splice, b2column, c2c_splice, base_plate, tension_member + + +# Create your views here. + +# Initialize Firebase Admin SDK +# TODO: Move this to a better place (e.g., AppConfig ready method) and use environment variables for path +if not firebase_admin._apps: + # Try to find the credential file + # Prefer service account placed in backend/firebase-service-account.json + repo_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) # .../backend + cred_candidates = [ + os.path.join(repo_root, "firebase-service-account.json"), + os.path.join(repo_root, "config", "firebase-service-account.json"), + "osdag_web/firebase-service-account.json", # legacy location + ] + cred_path = next((p for p in cred_candidates if os.path.exists(p)), None) + + if cred_path and os.path.exists(cred_path): + cred = credentials.Certificate(cred_path) + firebase_admin.initialize_app(cred) + else: + print("Warning: Firebase credentials not found. Checked:") + for p in cred_candidates: + print(f" - {p}") + + +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +def dashboard_view(request): + user = request.user + return Response({ + "message": f"Welcome {user.username}!", + "email": user.email, + }) + + +class FirebaseAuthView(APIView): + """ + Unified endpoint for all Firebase authentication (Google + Email/Password). + Syncs Firebase users to Django User model and returns user info. + """ + authentication_classes = [] # Bypass JWTAuthentication for this endpoint + permission_classes = [] # Public endpoint + + def post(self, request): + import logging + logger = logging.getLogger(__name__) + + # Log request data for debugging + logger.info(f"FirebaseAuthView POST request received. Data keys: {list(request.data.keys()) if hasattr(request.data, 'keys') else 'No data'}") + + token = request.data.get("token") + if not token: + logger.error("FirebaseAuthView: No token provided in request") + return Response({"error": "No token provided"}, status=400) + + logger.info(f"FirebaseAuthView: Token received (length: {len(token) if token else 0})") + + try: + decoded = firebase_auth.verify_id_token(token) + logger.info(f"FirebaseAuthView: Token verified successfully. UID: {decoded.get('uid')}, Email: {decoded.get('email')}") + + uid = decoded.get('uid') + email = decoded.get('email') + email_verified = decoded.get('email_verified', False) + + if not uid: + logger.error("FirebaseAuthView: Token decoded but missing UID") + return Response({"error": "Invalid token: missing UID"}, status=400) + + # Sync user to Django (use Firebase UID as username) + user, created = User.objects.get_or_create( + username=uid, + defaults={'email': email or ''} + ) + logger.info(f"FirebaseAuthView: User {'created' if created else 'retrieved'}. Username: {user.username}, Email: {user.email}") + + # Update email if changed + if email and user.email != email: + user.email = email + user.save() + logger.info(f"FirebaseAuthView: Updated user email to {email}") + + # Sync UserAccount if it doesn't exist + from apps.core.models import UserAccount + user_account, account_created = UserAccount.objects.get_or_create( + username=uid, + defaults={ + 'user': user, + 'email': email or '', + 'allInputValueFiles': [] + } + ) + logger.info(f"FirebaseAuthView: UserAccount {'created' if account_created else 'retrieved'}") + + # Update UserAccount if user link is missing or email changed + needs_save = False + if not user_account.user: + user_account.user = user + needs_save = True + if email and user_account.email != email: + user_account.email = email + needs_save = True + if user_account.user != user: + user_account.user = user + needs_save = True + + if needs_save: + user_account.save() + logger.info(f"FirebaseAuthView: UserAccount updated") + + logger.info(f"FirebaseAuthView: Authentication successful for UID: {uid}") + return Response({ + 'success': True, + 'uid': uid, + 'email': email, + 'email_verified': email_verified, + 'created': created, + 'message': 'Authentication successful' + }, status=200) + + except firebase_auth.InvalidIdTokenError as e: + logger.error(f"FirebaseAuthView: Invalid Firebase token error: {str(e)}") + return Response({"error": "Invalid Firebase token"}, status=400) + except firebase_auth.ExpiredIdTokenError as e: + logger.error(f"FirebaseAuthView: Expired Firebase token error: {str(e)}") + return Response({"error": "Firebase token has expired"}, status=400) + except Exception as e: + logger.error(f"FirebaseAuthView: Unexpected error: {str(e)}", exc_info=True) + return Response({"error": str(e)}, status=400) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([AllowAny]) +def get_design_types(request): + return JsonResponse({'result': design_type_data}, safe=False) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([AllowAny]) +def get_connections(request): + return JsonResponse({'result': connections_data}, safe=False) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([AllowAny]) +def get_shear_connection(request): + return JsonResponse({'result': shear_connection}, safe=False) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([AllowAny]) +def get_moment_connection(request): + return JsonResponse({'result': moment_connection}, safe=False) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([AllowAny]) +def get_b2b_splice(request): + return JsonResponse({'result': b2b_splice}, safe=False) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([AllowAny]) +def get_b2column(request): + return JsonResponse({'result': b2column}, safe=False) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([AllowAny]) +def get_c2c_splice(request): + return JsonResponse({'result': c2c_splice}, safe=False) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([AllowAny]) +def get_base_plate(request): + return JsonResponse({'result': base_plate}, safe=False) + + +@api_view(['GET']) +@authentication_classes([]) +@permission_classes([AllowAny]) +def get_tension_member(request): + return JsonResponse({'result': tension_member}, safe=False) + diff --git a/backend/apps/modules/__init__.py b/backend/apps/modules/__init__.py new file mode 100644 index 000000000..130f658cf --- /dev/null +++ b/backend/apps/modules/__init__.py @@ -0,0 +1,2 @@ +# Modules package + diff --git a/backend/apps/modules/base_plate/__init__.py b/backend/apps/modules/base_plate/__init__.py new file mode 100644 index 000000000..34df817c7 --- /dev/null +++ b/backend/apps/modules/base_plate/__init__.py @@ -0,0 +1,5 @@ +""" +Base Plate Module +""" +MODULE_ID = 'Base-Plate' +from .service import BasePlateService as Service diff --git a/backend/apps/modules/base_plate/adapter.py b/backend/apps/modules/base_plate/adapter.py new file mode 100644 index 000000000..e3386d70f --- /dev/null +++ b/backend/apps/modules/base_plate/adapter.py @@ -0,0 +1,420 @@ +""" +Base Plate Adapter +Maps web API payload to osdag_core BasePlateConnection keys and back. +""" +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl, +) +from osdag_core.design_type.connection.base_plate_connection import BasePlateConnection +from osdag_core.custom_logger import CustomLogger +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.Common import KEY_DISP_BASE_PLATE +from OCC.Core import BRepTools +from OCC.Core.Message import Message_ProgressRange +import sys +import os +from typing import Dict, Any, List +import traceback +import json + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + +# Core expects these key names (from osdag_core.Common / base_plate_connection.set_input_values) +KEY_CONN = 'Connectivity *' +KEY_END_CONDITION = 'End Condition' +KEY_SECSIZE = 'Member.Designation' +KEY_MATERIAL = 'Material' +KEY_SEC_MATERIAL = 'Member.Material' +KEY_AXIAL = 'Load.Axial' +KEY_AXIAL_BP = 'Load.Axial_Compression' +KEY_AXIAL_TENSION_BP = 'Load.Axial_Tension' +KEY_SHEAR = 'Load.Shear' +KEY_MOMENT = 'Load.Moment' +KEY_SHEAR_MAJOR = 'Load.Shear.Major' +KEY_SHEAR_MINOR = 'Load.Shear.Minor' +KEY_MOMENT_MAJOR = 'Load.Moment.Major' +KEY_MOMENT_MINOR = 'Load.Moment.Minor' +KEY_DIA_ANCHOR_OCF = 'Anchor Bolt.OCF.Diameter' +KEY_GRD_ANCHOR_OCF = 'Anchor Bolt.OCF.Grade' +KEY_DIA_ANCHOR_ICF = 'Anchor Bolt.ICF.Diameter' +KEY_GRD_ANCHOR_ICF = 'Anchor Bolt.ICF.Grade' +KEY_TYP_ANCHOR = 'Anchor Bolt.Type' +KEY_GRD_FOOTING = 'Footing.Grade' +KEY_WELD_TYPE = 'Weld.Type' +KEY_BASE_PLATE_MATERIAL = 'Base_Plate.Material' +KEY_ST_KEY_MATERIAL = 'Stiffener_Key.Material' +KEY_DP_ANCHOR_BOLT_DESIGNATION_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Designation' +KEY_DP_ANCHOR_BOLT_DESIGNATION_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Designation' +KEY_DP_ANCHOR_BOLT_TYPE_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Type' +KEY_DP_ANCHOR_BOLT_TYPE_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Type' +KEY_DP_ANCHOR_BOLT_GALVANIZED_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Galvanized' +KEY_DP_ANCHOR_BOLT_GALVANIZED_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Galvanized' +KEY_DP_ANCHOR_BOLT_HOLE_TYPE_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Bolt_Hole_Type' +KEY_DP_ANCHOR_BOLT_HOLE_TYPE_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Bolt_Hole_Type' +KEY_DP_ANCHOR_BOLT_LENGTH_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Length' +KEY_DP_ANCHOR_BOLT_LENGTH_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Length' +KEY_DP_ANCHOR_BOLT_MATERIAL_G_O_OCF = 'DesignPreferences.Anchor_Bolt.OCF.Material_Grade_OverWrite' +KEY_DP_ANCHOR_BOLT_MATERIAL_G_O_ICF = 'DesignPreferences.Anchor_Bolt.ICF.Material_Grade_OverWrite' +KEY_DP_ANCHOR_BOLT_FRICTION = 'DesignPreferences.Anchor_Bolt.Friction_coefficient' +KEY_DP_WELD_FAB = 'Weld.Fab' +KEY_DP_WELD_MATERIAL_G_O = 'Weld.Material_Grade_OverWrite' +KEY_DP_WELD_TYPE = 'Weld.Type' +KEY_DP_DETAILING_EDGE_TYPE = 'Detailing.Edge_type' +KEY_DP_DETAILING_CORROSIVE_INFLUENCES = 'Detailing.Corrosive_Influences' +KEY_DP_DESIGN_METHOD = 'Design.Design_Method' +KEY_DP_DESIGN_BASE_PLATE = 'DesignPreferences.Design.Base_Plate' + + +def get_required_keys() -> List[str]: + """Return required input parameters matching desktop Base Plate input dock only.""" + return [ + "Module", + "Connectivity", + "End Condition", + "Member.Designation", + "Material", + "Load.Axial", + "Load.Axial_Tension", + "Load.Shear.Major", + "Load.Shear.Minor", + "Load.Moment.Major", + "Load.Moment.Minor", + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for values; only keys present in desktop Base Plate input dock.""" + required_keys = get_required_keys() + missing_keys = contains_keys(input_values, required_keys) + if missing_keys is not None: + raise MissingKeyError(missing_keys[0]) + + str_keys = ["Module", "Material", "Connectivity", "End Condition"] + for key in str_keys: + if not isinstance(input_values.get(key), str): + raise InvalidInputTypeError(key, "str") + + load_keys = [ + "Load.Axial", "Load.Axial_Tension", + "Load.Shear.Major", "Load.Shear.Minor", + "Load.Moment.Major", "Load.Moment.Minor", + ] + for key in load_keys: + val = input_values.get(key) + if val is not None and val != "": + if not isinstance(val, str) or not float_able(val): + raise InvalidInputTypeError(key, "str (number)") + + member_designation = input_values.get("Member.Designation") + if isinstance(member_designation, list): + if not validate_list_type(member_designation, str): + raise InvalidInputTypeError("Member.Designation", "List[str]") + elif not isinstance(member_designation, str): + raise InvalidInputTypeError("Member.Designation", "str or List[str]") + + +def create_module() -> BasePlateConnection: + """Create an instance of the BasePlateConnection module design class and set it up for use""" + module = BasePlateConnection() + module.set_osdaglogger(None, id="web") + return module + + +def _ensure_list(val, default=None): + if val is None: + return default if default is not None else [] + if isinstance(val, list): + return val + return [val] if val != "" and val != "All" else [] + + +def _normalize_anchor_diameter(dia): + """Ensure core format: 'M20', 'M24' etc. Accepts 20, '20', 'M20'.""" + if dia is None or dia == "": + return "M20" + s = str(dia).strip() + if s.upper().startswith("M"): + return s if s[1:].isdigit() else "M20" + if s.isdigit(): + return f"M{int(s)}" + return "M20" + + +def _normalize_anchor_diameter_list(lst, default=None): + """Return list of anchor diameter strings in core format (e.g. ['M20']).""" + if not lst: + return [default or "M20"] + out = [] + for x in lst: + out.append(_normalize_anchor_diameter(x)) + return out if out else [default or "M20"] + + +def _first_or_str(val): + """Return first element if list, else string value. For single section used by core.""" + if isinstance(val, list): + return val[0] if len(val) > 0 else "Select Section" + if val == "All" or val == "[]" or (isinstance(val, str) and val.startswith("[") and val.endswith("]")): + return "Select Section" + return str(val) if val is not None else "Select Section" + + +def create_from_input(input_values: Dict[str, Any]) -> BasePlateConnection: + """Create BasePlateConnection from web input and run set_input_values with full design_dict.""" + module = create_module() + + member_designation = input_values.get("Member.Designation", "") + if isinstance(member_designation, str): + if member_designation in ("[]", "All", ""): + member_designation = [] + elif member_designation.startswith("[") and member_designation.endswith("]"): + try: + member_designation = json.loads(member_designation) + except Exception: + member_designation = [member_designation] + else: + member_designation = [member_designation] + elif not isinstance(member_designation, list): + member_designation = [str(member_designation)] + + # Single section string for core (core expects one section per design run) + section_str = _first_or_str(member_designation) if member_designation else "Select Section" + + material = input_values.get("Material", "E 250 (Fe 410 W)A") + if isinstance(material, dict): + material = material.get("Grade", material.get("grade", str(material))) + member_material = input_values.get("Member.Material", material) + if isinstance(member_material, dict): + member_material = member_material.get("Grade", member_material.get("grade", str(member_material))) + + # Anchor: core expects list of strings like ['M20'], ['8.8'] (IS 5624 format) + anchor_dia_ocf = input_values.get("Anchor.Diameter", input_values.get("Anchor.Diameter.OCF", input_values.get("Bolt.Diameter", []))) + anchor_grd_ocf = input_values.get("Anchor.Grade", input_values.get("Anchor.Grade.OCF", input_values.get("Bolt.Grade", []))) + anchor_dia_icf = input_values.get("Anchor.Diameter.ICF", anchor_dia_ocf) + anchor_grd_icf = input_values.get("Anchor.Grade.ICF", anchor_grd_ocf) + anchor_dia_ocf = _normalize_anchor_diameter_list(anchor_dia_ocf, "M20") + anchor_grd_ocf = _ensure_list(anchor_grd_ocf, ["8.8"]) + anchor_dia_icf = _normalize_anchor_diameter_list(anchor_dia_icf, "M20") + anchor_grd_icf = _ensure_list(anchor_grd_icf, anchor_grd_ocf) + + connectivity = input_values.get("Connectivity", input_values.get("Connectivity *", "Welded Column Base")) + axial = input_values.get("Load.Axial", "100") + shear = input_values.get("Load.Shear", "50") + shear_major = input_values.get("Load.Shear.Major", shear) + shear_minor = input_values.get("Load.Shear.Minor", "0") + moment = input_values.get("Load.Moment", "20") + moment_major = input_values.get("Load.Moment.Major", moment) + moment_minor = input_values.get("Load.Moment.Minor", "0") + axial_tension = input_values.get("Load.Axial_Tension", "0") + if connectivity == "Welded Column Base": + moment_major = "Disabled" + moment_minor = "Disabled" + if connectivity != "Moment Base Plate" or axial_tension == "": + axial_tension = "Disabled" + + footing_grade = input_values.get("Footing.Grade", "M20") + if footing_grade == "" or footing_grade is None: + footing_grade = "M20" + weld_type = input_values.get("Weld.Type", "Fillet Weld") + anchor_type = input_values.get("Anchor Bolt.Type", input_values.get("Anchor.Type", "End Plate Type")) + + # Design preferences defaults (desktop get_values_for_design_pref / init) + bp_material = input_values.get("Base_Plate.Material", member_material) + st_key_material = input_values.get("Stiffener_Key.Material", member_material) + length_ocf = input_values.get("DesignPreferences.Anchor_Bolt.OCF.Length", "0") + length_icf = input_values.get("DesignPreferences.Anchor_Bolt.ICF.Length", "0") + designation_ocf = input_values.get("DesignPreferences.Anchor_Bolt.OCF.Designation", "") + designation_icf = input_values.get("DesignPreferences.Anchor_Bolt.ICF.Designation", "") + if not designation_ocf and anchor_dia_ocf: + designation_ocf = f"{anchor_dia_ocf[0]}X{length_ocf} IS5624 GALV" + if not designation_icf and anchor_dia_icf: + designation_icf = f"{anchor_dia_icf[0]}X{length_icf} IS5624 GALV" + + # Parent MomentConnection.set_input_values() -> Load() requires numeric strings; use "0" when "Disabled" + shear_for_load = "0" if shear_major == "Disabled" else str(shear_major) + moment_for_load = "0" if moment_major == "Disabled" else str(moment_major) + design_dict = { + KEY_CONN: connectivity, + KEY_END_CONDITION: "Pinned", + KEY_SECSIZE: section_str, + KEY_MATERIAL: material, + KEY_SEC_MATERIAL: member_material, + KEY_AXIAL: str(axial), + KEY_AXIAL_BP: str(axial), + KEY_AXIAL_TENSION_BP: str(axial_tension), + KEY_SHEAR: shear_for_load, + KEY_MOMENT: moment_for_load, + KEY_SHEAR_MAJOR: str(shear_major), + KEY_SHEAR_MINOR: str(shear_minor), + KEY_MOMENT_MAJOR: str(moment_major), + KEY_MOMENT_MINOR: str(moment_minor), + KEY_DIA_ANCHOR_OCF: anchor_dia_ocf, + KEY_GRD_ANCHOR_OCF: anchor_grd_ocf, + KEY_DIA_ANCHOR_ICF: anchor_dia_icf, + KEY_GRD_ANCHOR_ICF: anchor_grd_icf, + KEY_TYP_ANCHOR: anchor_type, + KEY_GRD_FOOTING: str(footing_grade), + KEY_WELD_TYPE: weld_type, + KEY_BASE_PLATE_MATERIAL: bp_material, + KEY_ST_KEY_MATERIAL: st_key_material, + KEY_DP_ANCHOR_BOLT_DESIGNATION_OCF: designation_ocf or f"{anchor_dia_ocf[0] if anchor_dia_ocf else 20}X{length_ocf} IS5624 GALV", + KEY_DP_ANCHOR_BOLT_DESIGNATION_ICF: designation_icf or f"{anchor_dia_icf[0] if anchor_dia_icf else 20}X{length_icf} IS5624 GALV", + KEY_DP_ANCHOR_BOLT_TYPE_OCF: anchor_type, + KEY_DP_ANCHOR_BOLT_TYPE_ICF: anchor_type, + KEY_DP_ANCHOR_BOLT_GALVANIZED_OCF: input_values.get("DesignPreferences.Anchor_Bolt.OCF.Galvanized", "Yes"), + KEY_DP_ANCHOR_BOLT_GALVANIZED_ICF: input_values.get("DesignPreferences.Anchor_Bolt.ICF.Galvanized", "Yes"), + KEY_DP_ANCHOR_BOLT_HOLE_TYPE_OCF: input_values.get("DesignPreferences.Anchor_Bolt.OCF.Bolt_Hole_Type", "Over-sized"), + KEY_DP_ANCHOR_BOLT_HOLE_TYPE_ICF: input_values.get("DesignPreferences.Anchor_Bolt.ICF.Bolt_Hole_Type", "Over-sized"), + KEY_DP_ANCHOR_BOLT_LENGTH_OCF: str(length_ocf), + KEY_DP_ANCHOR_BOLT_LENGTH_ICF: str(length_icf), + KEY_DP_ANCHOR_BOLT_MATERIAL_G_O_OCF: float(input_values.get("DesignPreferences.Anchor_Bolt.OCF.Material_Grade_OverWrite", 0) or 0), + KEY_DP_ANCHOR_BOLT_MATERIAL_G_O_ICF: float(input_values.get("DesignPreferences.Anchor_Bolt.ICF.Material_Grade_OverWrite", 0) or 0), + KEY_DP_ANCHOR_BOLT_FRICTION: input_values.get("DesignPreferences.Anchor_Bolt.Friction_coefficient", "0.30"), + KEY_DP_WELD_FAB: input_values.get("Weld.Fab", "Shop Weld"), + KEY_DP_WELD_MATERIAL_G_O: float(input_values.get("Weld.Material_Grade_OverWrite", 0) or 0), + KEY_DP_WELD_TYPE: weld_type, + KEY_DP_DETAILING_EDGE_TYPE: input_values.get("Detailing.Edge_type", "b - Machine flame cut"), + KEY_DP_DETAILING_CORROSIVE_INFLUENCES: input_values.get("Detailing.Corrosive_Influences", "No"), + KEY_DP_DESIGN_METHOD: input_values.get("Design.Design_Method", "Limit State Design"), + KEY_DP_DESIGN_BASE_PLATE: input_values.get("DesignPreferences.Design.Base_Plate", "Effective Area Method"), + } + + try: + module.set_input_values(design_dict) + # Run the actual design (bp_parameters); desktop calls this via func_for_validation + validation_errors = module.func_for_validation(design_dict) + if validation_errors is not None and len(validation_errors) > 0: + raise ValueError(validation_errors[0] if validation_errors else "Validation failed") + except Exception as e: + traceback.print_exc() + raise + + return module + + +def _append_detail_list(output: dict, detail_list: list): + """Append TYPE_TEXTBOX entries from a detail method (e.g. stiffener_flange_details) into output.""" + if not detail_list: + return + for param in detail_list: + if len(param) >= 4: + key, label, param_type, value = param[0], param[1], param[2], param[3] + if key is None or param_type != "TextBox": + continue + if hasattr(value, "item"): + value = value.item() + output[key] = {"key": key, "label": label, "val": value} + + +def generate_output(input_values: Dict[str, Any]): + """Run design and return output dict + logs. Output keys match desktop output_values keys.""" + output = {} + module = create_from_input(input_values) + logs = [] + + try: + raw_output = module.output_values(True) + if hasattr(module, "logger") and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() + + for param in raw_output: + if len(param) >= 4: + key, label, param_type, value = param[0], param[1], param[2], param[3] + if key is None or param_type != "TextBox": + continue + if hasattr(value, "item"): + value = value.item() + output[key] = {"key": key, "label": label, "val": value} + + # Stiffener detail modals: merge flange, along web, across web details into output + if hasattr(module, "stiffener_flange_details"): + _append_detail_list(output, module.stiffener_flange_details(True)) + if hasattr(module, "stiffener_along_web_details"): + _append_detail_list(output, module.stiffener_along_web_details(True)) + if hasattr(module, "stiffener_across_web_details"): + _append_detail_list(output, module.stiffener_across_web_details(True)) + if hasattr(module, "stiffener_hollow_details"): + _append_detail_list(output, module.stiffener_hollow_details(True)) + except Exception as e: + traceback.print_exc() + raise + + return output, logs + + +BASE_PLATE_CAD_SECTIONS = ("Model", "Column", "Plate") + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate CAD model from input values as BREP/STL. Returns file path or empty string. + Desktop options: Model, Column, Base Plate (Plate). + """ + if section not in BASE_PLATE_CAD_SECTIONS: + raise InvalidInputTypeError( + "section", + "'Model', 'Column', 'Plate'", + ) + + try: + module = create_from_input(input_values) + except Exception as e: + traceback.print_exc() + print(f"[BasePlate CAD] create_from_input failed: {e}") + return "" + + try: + cdl = CommonDesignLogic( + None, None, "", KEY_DISP_BASE_PLATE, "Moment Connection", + ) + cdl.module_object = module + base_plate = cdl.createBasePlateCAD() + except Exception as e: + traceback.print_exc() + print(f"[BasePlate CAD] createBasePlateCAD failed: {e}") + return "" + + # Section -> getter on base_plate (desktop: Model, Column, Base Plate only) + section_getters = { + "Model": lambda: base_plate.get_models(), + "Column": lambda: base_plate.get_column_model(), + "Plate": lambda: base_plate.get_plate_connector_models(), + } + get_shape = section_getters.get(section) + if not get_shape: + return "" + + try: + model = get_shape() + except Exception as e: + traceback.print_exc() + print(f"[BasePlate CAD] getter for section '{section}' failed: {e}") + return "" + + if model is None: + print(f"[BasePlate CAD] No shape for section '{section}'; skipping write.") + return "" + + cad_dir = os.path.join(os.getcwd(), "file_storage", "cad_models") + os.makedirs(cad_dir, exist_ok=True) + section_safe = section.replace(" ", "_") + file_name = f"{session}_{section_safe}.brep" + file_path = os.path.join("file_storage", "cad_models", file_name) + + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) + except Exception as e: + print(f"[BasePlate CAD] BREP write failed for {section}: {e}") + return "" + + try: + stl_path = os.path.join(os.getcwd(), file_path.replace(".brep", ".stl")) + write_stl(model, stl_path) + except Exception as stle: + print(f"[BasePlate CAD] Warning: STL write failed for {section}: {stle}") + + return file_path diff --git a/backend/apps/modules/base_plate/service.py b/backend/apps/modules/base_plate/service.py new file mode 100644 index 000000000..faae7af12 --- /dev/null +++ b/backend/apps/modules/base_plate/service.py @@ -0,0 +1,93 @@ +""" +Base Plate Service - Business logic layer +Bridges between API and osdag_core +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class BasePlateService: + """Service class for Base Plate module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """ + Run design calculation and return results. + + Args: + inputs: Dictionary of input parameters + request: Optional Django request object (for future use) + project_id: Optional project ID (for future use) + user_email: Optional user email (for future use) + + Returns: + Dictionary with 'data' (results) and 'logs' (calculation logs) + """ + print("=" * 60) + print("BasePlateService.calculate() called") + print("=" * 60) + print(f"Inputs received: {list(inputs.keys())[:10]}...") # Print first 10 keys + + try: + # Validate inputs + print("\n[1/3] Validating inputs...") + validate_input(inputs) + print("✅ Input validation passed") + + # Generate formatted output (this handles module creation and calculation) + print("\n[2/3] Generating output (creates module and runs calculation)...") + output, logs = generate_output(inputs) + print(f"✅ Output generated: {len(output)} output parameters") + print(f"✅ Logs retrieved: {len(logs) if logs else 0} log entries") + + print("\n[3/3] Preparing response...") + result = { + 'data': output, + 'logs': logs or [], # Ensure logs is always a list + 'success': True + } + print("✅ Response prepared successfully") + print("=" * 60) + + return result + + except Exception as e: + print("\n" + "=" * 60) + print("ERROR in BasePlateService.calculate()") + print("=" * 60) + print(f"Exception type: {type(e).__name__}") + print(f"Exception message: {str(e)}") + + # Safely extract error message + error_msg = str(e) + if hasattr(e, 'error') and e.error is not None: + error_msg = str(e.error) + elif hasattr(e, 'args') and len(e.args) > 0: + error_msg = str(e.args[0]) + + print(f"Final error message: {error_msg}") + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60) + + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': error_msg + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) diff --git a/backend/apps/modules/base_plate/urls.py b/backend/apps/modules/base_plate/urls.py new file mode 100644 index 000000000..4e4f53c4d --- /dev/null +++ b/backend/apps/modules/base_plate/urls.py @@ -0,0 +1,11 @@ +""" +Base Plate URLs +""" +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import BasePlateViewSet + +router = DefaultRouter() +router.register(r'', BasePlateViewSet, basename='base-plate') + +urlpatterns = router.urls diff --git a/backend/apps/modules/base_plate/views.py b/backend/apps/modules/base_plate/views.py new file mode 100644 index 000000000..0aa892396 --- /dev/null +++ b/backend/apps/modules/base_plate/views.py @@ -0,0 +1,168 @@ +""" +Base Plate ViewSet - design, options, and CAD endpoints. +""" +import logging +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.permissions import AllowAny +from rest_framework.response import Response + +from apps.core.models import Material, CustomMaterials, Columns, Beams +from apps.core.utils.module_helpers import handle_design_request +from apps.core.utils.errors import format_error_response, get_error_status_code +from apps.core.utils.cad_helpers import generate_cad_models, get_default_sections + +from .service import BasePlateService +from .adapter import create_from_input + +logger = logging.getLogger(__name__) + +# Desktop-matching option lists (Common.py VALUES_*) +CONNECTIVITY_LIST = ['Welded Column Base', 'Moment Base Plate', 'Hollow/Tubular Column Base'] +FOOTING_GRADE_LIST = ['Select Grade', 'M10', 'M15', 'M20', 'M25', 'M30', 'M35', 'M40', 'M45', 'M50', 'M55'] +ANCHOR_TYPE_LIST = ['End Plate Type', 'IS 5624-Type A', 'IS 5624-Type B'] +WELD_TYPE_LIST = ['Fillet Weld'] +# Anchor diameters: IS 5624 notation (must match osdag_core other_standards.table1 keys: M8..M72) +ANCHOR_DIAMETER_LIST = ['M8', 'M10', 'M12', 'M16', 'M20', 'M24', 'M30', 'M36', 'M42', 'M48', 'M56', 'M64', 'M72'] +# Match desktop IS1367_Part3_2002.get_bolt_PC() (Table 1 of IS 1367 Part-3:2002) +ANCHOR_GRADE_LIST = ['3.6', '4.6', '4.8', '5.6', '5.8', '6.8', '8.8', '9.8', '10.9', '12.9'] + + +class BasePlateViewSet(viewsets.ViewSet): + permission_classes = [AllowAny] + + @action(detail=False, methods=['post'], url_path='design') + def design(self, request): + """ + POST /api/modules/base-plate/design/ + Request body: { "inputs": {...} } or raw dict. + """ + inputs = request.data.get('inputs', request.data) + project_id = request.data.get('project_id') + + context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug='base-plate', + module_name='base-plate', + ) + + try: + result = BasePlateService.calculate( + inputs=inputs, + request=request, + project_id=project_id if not context.get('is_guest') else None, + user_email=context.get('user_email'), + ) + + if context.get('project_result'): + result['project_saved'] = context['project_result'].get('saved') + if context['project_result'].get('project_id'): + result['project_id'] = context['project_result']['project_id'] + if context['project_result'].get('error'): + result['project_error'] = context['project_result']['error'] + + status_code = 200 if result.get('success', True) else 400 + return Response(result, status=status_code) + except Exception as exc: + logger.error(f"Error in base plate design: {exc}", exc_info=True) + error_response = format_error_response(exc) + status_code = get_error_status_code(exc) + return Response(error_response, status=status_code) + + @action(detail=False, methods=['get'], url_path='options') + def options(self, request): + """ + GET /api/modules/base-plate/options/ + Returns dropdown/options data for base plate (sectionDesignation, materialList, etc.). + """ + email = request.query_params.get('email') + + def material_list(): + mats = list(Material.objects.all().values()) + if email: + mats += list(CustomMaterials.objects.filter(email=email).values()) + mats.append({'id': -1, 'Grade': 'Custom'}) + return mats + + # Beams and Columns: combine column and beam designations for section list (desktop VALUE_BEAM_COL) + def section_designation(): + cols = list(Columns.objects.values_list('Designation', flat=True).distinct()) + beams = list(Beams.objects.values_list('Designation', flat=True).distinct()) + combined = sorted(set(cols) | set(beams)) + return combined + + try: + data = { + 'sectionDesignation': section_designation(), + 'profileList': ['Columns', 'Beams'], + 'materialList': material_list(), + 'anchorDiameterList': ANCHOR_DIAMETER_LIST, + 'anchorGradeList': ANCHOR_GRADE_LIST, + 'footingGradeList': FOOTING_GRADE_LIST, + 'connectivityList': CONNECTIVITY_LIST, + 'weldTypeList': WELD_TYPE_LIST, + 'anchorTypeList': ANCHOR_TYPE_LIST, + } + return Response(data, status=status.HTTP_200_OK) + except Exception as exc: + logger.exception("Base plate options failed") + return Response({'error': str(exc)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + @action(detail=False, methods=['post'], url_path='cad') + def cad(self, request): + """ + POST /api/modules/base-plate/cad/ + Request body: { "inputs": {...}, "sections": ["Model", "Plate", ...] } (sections optional). + Returns: { "status": "success"|"coming_soon", "files": {...}, "hover_dict": {...}, "warnings": [...] } + """ + inputs = request.data.get('inputs', request.data) + if not inputs: + return Response({'error': 'inputs are required'}, status=status.HTTP_400_BAD_REQUEST) + sections = request.data.get('sections') + if not sections: + sections = get_default_sections('base-plate', 'base-plate') + if not sections: + return Response( + {'error': 'No sections defined for base-plate'}, + status=status.HTTP_400_BAD_REQUEST, + ) + try: + result = generate_cad_models( + service_class=BasePlateService, + inputs=inputs, + sections=sections, + create_from_input_func=create_from_input, + ) + if not result['files']: + return Response( + { + 'status': 'coming_soon', + 'message': '3D model generation is coming soon for this module', + 'files': {}, + 'hover_dict': result.get('hover_dict', {}), + }, + status=status.HTTP_200_OK, + ) + return Response( + { + 'status': 'success', + 'files': result['files'], + 'hover_dict': result['hover_dict'], + 'message': 'CAD models generated successfully', + 'warnings': result.get('warnings', []), + }, + status=status.HTTP_201_CREATED, + ) + except Exception as exc: + logger.exception("Base plate CAD failed: %s", exc) + return Response( + { + 'status': 'coming_soon', + 'message': '3D model generation is coming soon for this module', + 'files': {}, + 'hover_dict': {}, + }, + status=status.HTTP_200_OK, + ) diff --git a/backend/apps/modules/compression_member/__init__.py b/backend/apps/modules/compression_member/__init__.py new file mode 100644 index 000000000..5ce89b843 --- /dev/null +++ b/backend/apps/modules/compression_member/__init__.py @@ -0,0 +1,5 @@ +""" +Compression Member Module +""" +MODULE_ID = 'Compression-Member' +from .service import CompressionMemberService as Service diff --git a/backend/apps/modules/compression_member/adapter.py b/backend/apps/modules/compression_member/adapter.py new file mode 100644 index 000000000..5a8766def --- /dev/null +++ b/backend/apps/modules/compression_member/adapter.py @@ -0,0 +1,189 @@ +""" +Compression Member Adapter +Implements the business logic directly (not re-exporting from osdag_api) +""" +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type +) +from osdag_core.design_type.compression_member.compression import Compression +from osdag_core.custom_logger import CustomLogger +import sys +import os +from typing import Dict, Any, List +import traceback +import json + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + """Return all required input parameters for the compression member module.""" + return [ + "Module", # KEY_MODULE + "Member.Profile", # KEY_SEC_PROFILE + "Member.Designation", # KEY_SECSIZE + "Material", # KEY_MATERIAL + "Member.Material", # KEY_SEC_MATERIAL + "Member.Length", # KEY_LENGTH + "Load.Axial", # KEY_AXIAL + "End_1", # KEY_END1 + "End_2", # KEY_END2 + "Design.Design_Method", # KEY_DP_DESIGN_METHOD + "Conn_Location", # KEY_LOCATION + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + required_keys = get_required_keys() + missing_keys = contains_keys(input_values, required_keys) + if missing_keys is not None: + raise MissingKeyError(missing_keys[0]) + + # Validate string keys + str_keys = [ + "Module", + "Member.Profile", + "Material", + "Member.Material", + "Design.Design_Method", + "Conn_Location", + "End_1", + "End_2", + ] + for key in str_keys: + if not isinstance(input_values.get(key), str): + raise InvalidInputTypeError(key, "str") + + # Validate numeric keys + if not isinstance(input_values.get("Member.Length"), str) or not int_able(input_values.get("Member.Length")): + raise InvalidInputTypeError("Member.Length", "str where str can be converted to int") + + if not isinstance(input_values.get("Load.Axial"), str) or not float_able(input_values.get("Load.Axial")): + raise InvalidInputTypeError("Load.Axial", "str where str can be converted to float") + + # Validate Member.Designation (can be list or string) + member_designation = input_values.get("Member.Designation") + if isinstance(member_designation, list): + if not validate_list_type(member_designation, str): + raise InvalidInputTypeError("Member.Designation", "List[str]") + elif not isinstance(member_designation, str): + raise InvalidInputTypeError("Member.Designation", "str or List[str]") + + +def create_module() -> Compression: + """Create an instance of the Compression module design class and set it up for use""" + module = Compression() + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> Compression: + """Create an instance of the Compression module design class from input values.""" + module = None + try: + module = create_module() + except Exception as e: + print('Error in create_module:', e) + raise + + # Handle array conversion for Member.Designation + member_designation = input_values.get('Member.Designation', '') + if isinstance(member_designation, str): + if member_designation == '[]' or member_designation == 'All': + member_designation = [] + elif member_designation.startswith('[') and member_designation.endswith(']'): + try: + member_designation = json.loads(member_designation) + except: + member_designation = [member_designation] + elif not isinstance(member_designation, list): + member_designation = [str(member_designation)] + + # Map input_values to exact keys expected by Compression.set_input_values + design_dict = { + 'Module': input_values.get('Module', 'Struts in Trusses'), + 'Member.Profile': input_values.get('Member.Profile', 'Angles'), + 'Member.Designation': member_designation, + 'Material': input_values.get('Material', 'E 250 (Fe 410 W)A'), + 'Member.Material': input_values.get('Member.Material', 'E 250 (Fe 410 W)A'), + 'Member.Length': input_values.get('Member.Length', '3000'), + 'Load.Axial': input_values.get('Load.Axial', '100'), + 'End_1': input_values.get('End_1', 'Fixed'), + 'End_2': input_values.get('End_2', 'Fixed'), + 'Design.Design_Method': input_values.get('Design.Design_Method', 'Limit State Design'), + 'Conn_Location': input_values.get('Conn_Location', input_values.get('Location', 'Long Leg')), + # Design preference keys with defaults + 'Optimum.AllowUR': input_values.get('Optimum.AllowUR', '1.0'), + 'Effective.Area_Para': input_values.get('Effective.Area_Para', '1.0'), + ' In_Plane': input_values.get(' In_Plane', '1.0'), + ' Out_of_Plane': input_values.get(' Out_of_Plane', '1.0'), + 'Load.Type': input_values.get('Load.Type', 'Concentric Load'), + 'Bolt.Number': input_values.get('Bolt.Number', '1.0'), + 'Connector.Plate.Thickness_List': input_values.get('Connector.Plate.Thickness_List', '8'), + } + + try: + if module is None: + raise RuntimeError('Module instance was not created') + module.set_input_values(design_dict) + except Exception as e: + traceback.print_exc() + print('Error in set_input_values:', e) + raise + + return module + + +def generate_output(input_values: Dict[str, Any]): + """ + Generate, format and return the output values from the given input values. + """ + output = {} + module = create_from_input(input_values) + logs = [] + + try: + # Generate output values + raw_output_text = module.output_values(True) + + # Get logs from the custom logger + if hasattr(module, 'logger') and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() + + raw_output = raw_output_text + + # Process each parameter + for i, param in enumerate(raw_output): + if len(param) >= 4: + key = param[0] + label = param[1] + param_type = param[2] + value = param[3] + + # Check if it's a TextBox type and has a valid key + if param_type == "TextBox" and key is not None: + # Handle numpy types + if hasattr(value, 'item'): + value = value.item() + + output[key] = { + "key": key, + "label": label, + "val": value + } + except Exception as e: + print(f'Error in generate_output: {e}') + traceback.print_exc() + + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + # For now, returning empty string as CAD generation might need specific implementation + return "" diff --git a/backend/apps/modules/compression_member/registry.py b/backend/apps/modules/compression_member/registry.py new file mode 100644 index 000000000..f13d0cfe4 --- /dev/null +++ b/backend/apps/modules/compression_member/registry.py @@ -0,0 +1,26 @@ +""" +Compression Member Registry - Auto-discovers sub-modules +Inherits from BaseModuleRegistry (DRY principle) +""" +import os +from apps.core.registry import BaseModuleRegistry + + +class CompressionMemberRegistry(BaseModuleRegistry): + """Registry for compression member sub-modules""" + # Each registry needs its own dictionaries (class-level inheritance quirk) + _registry = {} + _module_id_map = {} + + +# Auto-discover sub-modules +print("[CompressionMemberRegistry] Starting auto_discover...") +_package_name = 'apps.modules.compression_member.submodules' +_package_path = os.path.join(os.path.dirname(__file__), 'submodules') +print(f"[CompressionMemberRegistry] Package path: {_package_path}") +CompressionMemberRegistry.auto_discover(_package_name, _package_path) + +# Debug: print registered modules +print(f"[CompressionMemberRegistry] Registered slugs: {list(CompressionMemberRegistry._registry.keys())}") +print(f"[CompressionMemberRegistry] Module ID map: {CompressionMemberRegistry._module_id_map}") + diff --git a/backend/apps/modules/compression_member/service.py b/backend/apps/modules/compression_member/service.py new file mode 100644 index 000000000..f9f65f357 --- /dev/null +++ b/backend/apps/modules/compression_member/service.py @@ -0,0 +1,93 @@ +""" +Compression Member Service - Business logic layer +Bridges between API and osdag_core +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class CompressionMemberService: + """Service class for Compression Member module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """ + Run design calculation and return results. + + Args: + inputs: Dictionary of input parameters + request: Optional Django request object (for future use) + project_id: Optional project ID (for future use) + user_email: Optional user email (for future use) + + Returns: + Dictionary with 'data' (results) and 'logs' (calculation logs) + """ + print("=" * 60) + print("CompressionMemberService.calculate() called") + print("=" * 60) + print(f"Inputs received: {list(inputs.keys())[:10]}...") # Print first 10 keys + + try: + # Validate inputs + print("\n[1/3] Validating inputs...") + validate_input(inputs) + print("✅ Input validation passed") + + # Generate formatted output (this handles module creation and calculation) + print("\n[2/3] Generating output (creates module and runs calculation)...") + output, logs = generate_output(inputs) + print(f"✅ Output generated: {len(output)} output parameters") + print(f"✅ Logs retrieved: {len(logs) if logs else 0} log entries") + + print("\n[3/3] Preparing response...") + result = { + 'data': output, + 'logs': logs or [], # Ensure logs is always a list + 'success': True + } + print("✅ Response prepared successfully") + print("=" * 60) + + return result + + except Exception as e: + print("\n" + "=" * 60) + print("ERROR in CompressionMemberService.calculate()") + print("=" * 60) + print(f"Exception type: {type(e).__name__}") + print(f"Exception message: {str(e)}") + + # Safely extract error message + error_msg = str(e) + if hasattr(e, 'error') and e.error is not None: + error_msg = str(e.error) + elif hasattr(e, 'args') and len(e.args) > 0: + error_msg = str(e.args[0]) + + print(f"Final error message: {error_msg}") + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60) + + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': error_msg + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) diff --git a/backend/apps/modules/compression_member/submodules/__init__.py b/backend/apps/modules/compression_member/submodules/__init__.py new file mode 100644 index 000000000..c1084ccc2 --- /dev/null +++ b/backend/apps/modules/compression_member/submodules/__init__.py @@ -0,0 +1 @@ +# Submodules for Compression Member diff --git a/backend/apps/modules/compression_member/submodules/axially_loaded_column/__init__.py b/backend/apps/modules/compression_member/submodules/axially_loaded_column/__init__.py new file mode 100644 index 000000000..52db73eb4 --- /dev/null +++ b/backend/apps/modules/compression_member/submodules/axially_loaded_column/__init__.py @@ -0,0 +1,12 @@ +""" +Axially Loaded Column - Compression Member Submodule + +MODULE_ID follows the legacy identifier used throughout Osdag for this design type. +Service provides the high-level API used by the compression_member viewset. +""" + +from .service import AxiallyLoadedColumnService + +MODULE_ID = "Axially-Loaded-Column" +Service = AxiallyLoadedColumnService + diff --git a/backend/apps/modules/compression_member/submodules/axially_loaded_column/adapter.py b/backend/apps/modules/compression_member/submodules/axially_loaded_column/adapter.py new file mode 100644 index 000000000..f88c511b5 --- /dev/null +++ b/backend/apps/modules/compression_member/submodules/axially_loaded_column/adapter.py @@ -0,0 +1,363 @@ +""" +Axially Loaded Column - Design Adapter + +Implements the business logic bridge between the generic module API and +the core osdag_web ColumnDesign implementation for axially loaded columns. + +This adapter: + - Validates inputs for type and presence. + - Maps flat input dicts into ColumnDesign.set_input_values. + - Runs the design and flattens ColumnDesign.output_values into + { key: { key, label, val } } for the web frontend. + - Generates CAD models using osdag_core.cad.common_logic.CommonDesignLogic. +""" + +from typing import Dict, Any, List, Tuple +import os +import traceback + +from apps.core.utils import ( + MissingKeyError, + InvalidInputTypeError, +) + +from osdag_core.design_type.compression_member.compression_column import ColumnDesign +from osdag_core.cad.common_logic import CommonDesignLogic + +try: + from OCC.Core.BRepTools import breptools_Write + from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs + from OCC.Core.StlAPI import StlAPI_Writer +except ImportError: # pragma: no cover - OCC may not be present in all environments + breptools_Write = None + STEPControl_Writer = None + STEPControl_AsIs = None + StlAPI_Writer = None + StlAPI_Writer = None + + +def get_required_keys() -> List[str]: + """ + Return all required input parameters for the module. + + Keys are chosen to match the frontend payload and what ColumnDesign + expects in its design dictionary. + """ + return [ + "Module", + "Member.Profile", + "Member.Designation", + "Material", + "Actual.Length_zz", + "Actual.Length_yy", + "End_1", + "End_2", + "End_1_Y", + "End_2_Y", + "Load.Axial", + ] + + +def _ensure_numeric(name: str, value: Any) -> float: + """ + Convert a value to float, raising InvalidInputTypeError on failure. + """ + if isinstance(value, (int, float)): + return float(value) + if isinstance(value, str): + try: + return float(value) + except ValueError: + raise InvalidInputTypeError( + name, "numeric (int/float) or numeric string" + ) + raise InvalidInputTypeError( + name, "numeric (int/float) or numeric string" + ) + + +def validate_input(input_values: Dict[str, Any]) -> None: + """ + Validate that all required keys are present and of the correct type. + + This is intentionally strict to keep runtime errors closer to the + user input, mirroring the behaviour of other adapters. + """ + required = get_required_keys() + missing = [k for k in required if k not in input_values] + if missing: + # raise the first missing key, consistent with other modules + raise MissingKeyError(missing[0]) + + # String keys that must be simple strings + str_keys = [ + "Module", + "Member.Profile", + "Material", + "End_1", + "End_2", + "End_1_Y", + "End_2_Y", + ] + for key in str_keys: + val = input_values.get(key) + if not isinstance(val, str): + raise InvalidInputTypeError(key, "str") + + # Member.Designation can be string or list[str] + md = input_values.get("Member.Designation") + if not isinstance(md, (str, list)): + raise InvalidInputTypeError("Member.Designation", "str or List[str]") + if isinstance(md, list): + for item in md: + if not isinstance(item, str): + raise InvalidInputTypeError( + "Member.Designation", + "List[str] where each item is a string", + ) + + # Numeric keys: lengths and axial load + for key in ["Actual.Length_zz", "Actual.Length_yy", "Load.Axial"]: + _ensure_numeric(key, input_values.get(key)) + + # Basic non-empty profile check + if not input_values.get("Member.Profile"): + raise InvalidInputTypeError("Member.Profile", "non-empty str") + + +def create_module() -> ColumnDesign: + """ + Create and initialize a ColumnDesign instance for web usage. + """ + module = ColumnDesign() + # Use a generic logger id; the id string is only to keep instances distinct. + try: + module.set_osdaglogger(None, id="web") + except Exception: + # If logger setup fails, we still allow design to proceed. + traceback.print_exc() + return module + + +def create_from_input(input_values: Dict[str, Any]) -> ColumnDesign: + """ + Create a ColumnDesign instance and populate it from flat input values. + + This function is the single place where we map frontend keys into the + design dictionary expected by ColumnDesign.set_input_values. + """ + # Validate strictly first + validate_input(input_values) + + # Build a design dictionary matching compression_column.ColumnDesign expectations + mapped: Dict[str, Any] = { + "Module": input_values.get("Module", "Axially-Loaded-Column"), + "Member.Profile": input_values.get("Member.Profile"), + "Member.Designation": input_values.get("Member.Designation"), + "Member.Material": input_values.get("Material"), + # ColumnDesign uses unsupported lengths as base inputs; we also keep the + # "Actual" keys around for completeness. + "Actual.Length_zz": _ensure_numeric( + "Actual.Length_zz", input_values.get("Actual.Length_zz") + ), + "Actual.Length_yy": _ensure_numeric( + "Actual.Length_yy", input_values.get("Actual.Length_yy") + ), + "Unsupported.Length_zz": _ensure_numeric( + "Actual.Length_zz", input_values.get("Actual.Length_zz") + ), + "Unsupported.Length_yy": _ensure_numeric( + "Actual.Length_yy", input_values.get("Actual.Length_yy") + ), + "End_1": input_values.get("End_1"), + "End_2": input_values.get("End_2"), + "End_1_Y": input_values.get("End_1_Y"), + "End_2_Y": input_values.get("End_2_Y"), + "Load.Axial": _ensure_numeric( + "Load.Axial", input_values.get("Load.Axial") + ), + "Optimum.AllowUR": str(input_values.get("Optimum.AllowUR", "1.0")), + "Effective.Area_Para": str(input_values.get("Effective.Area_Para", "1.0")), + "Optimum.Para": str(input_values.get("Optimum.Para", "Utilization Ratio")), + "Steel.Cost": str(input_values.get("Steel.Cost", "50")), + "Design.Design_Method": str(input_values.get("Design.Design_Method", "Limit State Design")), + } + + module = create_module() + module.set_input_values(mapped) + return module + + +def generate_output( + input_values: Dict[str, Any] +) -> Tuple[Dict[str, Any], List[str]]: + """ + Run the ColumnDesign calculation and flatten its outputs. + + Returns: + output: dict of { key: { key, label, val } } + logs: list of log strings + """ + output: Dict[str, Any] = {} + logs: List[str] = [] + + try: + module = create_from_input(input_values) + except Exception as e: + traceback.print_exc() + return {}, [f"Error while creating ColumnDesign: {e}"] + + try: + raw_output = module.output_values(True) + except Exception as e: + traceback.print_exc() + return {}, [f"Error running ColumnDesign.output_values: {e}"] + + # Flatten only TextBox parameters + for param in raw_output: + if not isinstance(param, (list, tuple)) or len(param) < 4: + continue + key = param[0] + label = param[1] + ptype = param[2] + val = param[3] + if key is None or ptype != "TextBox": + continue + # Unwrap numpy scalars if present + if hasattr(val, "item"): + try: + val = val.item() + except Exception: + pass + output[key] = {"key": key, "label": label, "val": val} + + # Logs from CustomLogger if available + try: + if hasattr(module, "logger") and hasattr(module.logger, "get_logs"): + logs = module.logger.get_logs() or [] + else: + logs = ["Axially Loaded Column computation completed."] + except Exception: + traceback.print_exc() + logs = ["Axially Loaded Column computation completed."] + + return output, logs + + +def create_cad_model( + input_values: Dict[str, Any], section: str, session: str +) -> str: + """ + Generate a CAD model for the given section and return the file path. + + Args: + input_values: Design input parameters (same dict used for design). + section: One of 'Model' or 'Column' (others are rejected). + session: Session identifier (used to build file names). + """ + if section not in ("Model", "Column"): + raise InvalidInputTypeError( + "section", "'Model' or 'Column'" + ) + + try: + module = create_from_input(input_values) + except Exception as e: + traceback.print_exc() + raise InvalidInputTypeError( + "input_values", f"Invalid design inputs for CAD generation: {e}" + ) + + base_tmp = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "tmp_cad", + ) + session_folder = os.path.join(base_tmp, session) + os.makedirs(session_folder, exist_ok=True) + + brep_path = os.path.join(session_folder, f"{section}.brep") + stl_path = os.path.join(session_folder, f"{section}.stl") + step_path = os.path.join(session_folder, f"{section}.step") + + # Try to obtain a shape from ColumnDesign via CommonDesignLogic + final_shape = None + try: + try: + cd = CommonDesignLogic( + None, None, "", module.module if hasattr(module, "module") else "", module.mainmodule + ) + except Exception: + cd = CommonDesignLogic(None, None, "", "", getattr(module, "mainmodule", None)) + + # Best-effort: call the 3D setup and 2D cad creation + try: + module.call_3DModel(True, module.__class__) + except Exception: + pass + + try: + cd.module = getattr(module, "module", "") + cd.mainmodule = getattr(module, "mainmodule", None) + cd.module_object = module + cd.component = section + final_shape = cd.create2Dcad() + except Exception: + final_shape = None + except Exception: + traceback.print_exc() + final_shape = None + + # Fallback: if no shape, write a small placeholder file + if final_shape is None: + with open(brep_path, "w", encoding="utf-8") as f: + f.write("No CAD model produced for this input (empty).") + return brep_path + + wrote_any = False + + # Prefer CommonDesignLogic's writers if available + try: + if hasattr(CommonDesignLogic, "write_brep"): + cd.write_brep(final_shape, brep_path) # type: ignore[attr-defined] + wrote_any = True + except Exception: + traceback.print_exc() + + # Fallback: pythonOCC writers + if not wrote_any and breptools_Write is not None: + try: + breptools_Write(final_shape, brep_path) + wrote_any = True + except Exception: + traceback.print_exc() + + if not wrote_any and STEPControl_Writer is not None: + try: + writer = STEPControl_Writer() + writer.Transfer(final_shape, STEPControl_AsIs) + writer.Write(step_path) + wrote_any = os.path.exists(step_path) + except Exception: + traceback.print_exc() + + # Try writing STL as well + if StlAPI_Writer is not None: + try: + stl_writer = StlAPI_Writer() + ok = stl_writer.Write(final_shape, stl_path) + wrote_any = wrote_any or ok + except Exception: + traceback.print_exc() + + # Final fallback: if nothing wrote, return a tiny placeholder BREP + if not wrote_any: + with open(brep_path, "w", encoding="utf-8") as f: + f.write("Failed to export true CAD model. This is a fallback placeholder.") + + # Prefer STL > STEP > BREP + if os.path.exists(stl_path): + return stl_path + if os.path.exists(step_path): + return step_path + return brep_path + diff --git a/backend/apps/modules/compression_member/submodules/axially_loaded_column/service.py b/backend/apps/modules/compression_member/submodules/axially_loaded_column/service.py new file mode 100644 index 000000000..823c46022 --- /dev/null +++ b/backend/apps/modules/compression_member/submodules/axially_loaded_column/service.py @@ -0,0 +1,99 @@ +""" +Axially Loaded Column - Service Layer +Bridges between the REST API and osdag_core ColumnDesign via the local adapter. +""" + +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class AxiallyLoadedColumnService: + """Service class for Axially-Loaded-Column module.""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """ + Run design calculation and return results. + + Args: + inputs: Dictionary of input parameters. + request: Optional Django request object (unused for now). + project_id: Optional project ID (unused for now; handled at higher layer). + user_email: Optional user email (unused for now; handled at higher layer). + + Returns: + dict with: + - data: flattened output dict { key: {key,label,val} } + - logs: list of log strings + - success: bool + - error: optional error message on failure + """ + print("=" * 60) + print("AxiallyLoadedColumnService.calculate() called") + print("=" * 60) + print(f"Inputs received keys (first 10): {list(inputs.keys())[:10]}") + + try: + # 1) Validate inputs + print("\n[1/3] Validating inputs...") + validate_input(inputs) + print("✅ Input validation passed") + + # 2) Generate output (creates ColumnDesign instance and runs calculation) + print("\n[2/3] Generating output from ColumnDesign...") + output, logs = generate_output(inputs) + print(f"✅ Output generated: {len(output)} parameters") + print(f"✅ Logs count: {len(logs) if logs else 0}") + + # 3) Prepare response + print("\n[3/3] Preparing response payload...") + result = { + "data": output, + "logs": logs or [], + "success": True, + } + print("✅ AxiallyLoadedColumnService.calculate() completed successfully") + print("=" * 60) + return result + + except Exception as e: + print("\n" + "=" * 60) + print("❌ ERROR in AxiallyLoadedColumnService.calculate()") + print("=" * 60) + print(f"Exception type: {type(e).__name__}") + print(f"Exception message: {str(e)}") + + # Derive a clean error message + error_msg = str(e) + if hasattr(e, "error") and e.error is not None: + error_msg = str(e.error) + elif hasattr(e, "args") and e.args: + error_msg = str(e.args[0]) + + print(f"Final error message: {error_msg}") + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60) + + return { + "data": {}, + "logs": [], + "success": False, + "error": error_msg, + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters. + section: Section name ('Model', 'Column', etc.). + session: Session identifier for file naming. + + Returns: + File path to the generated CAD model (relative to project root). + """ + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/compression_member/submodules/struts_bolted/__init__.py b/backend/apps/modules/compression_member/submodules/struts_bolted/__init__.py new file mode 100644 index 000000000..3b6029791 --- /dev/null +++ b/backend/apps/modules/compression_member/submodules/struts_bolted/__init__.py @@ -0,0 +1,8 @@ +""" +Struts Bolted to End Gusset - Submodule Init +""" +from .service import StrutsBoltedService + +# Required exports for BaseModuleRegistry auto_discover +MODULE_ID = 'Struts-Bolted-Design' +Service = StrutsBoltedService diff --git a/backend/apps/modules/compression_member/submodules/struts_bolted/adapter.py b/backend/apps/modules/compression_member/submodules/struts_bolted/adapter.py new file mode 100644 index 000000000..b0d5868d5 --- /dev/null +++ b/backend/apps/modules/compression_member/submodules/struts_bolted/adapter.py @@ -0,0 +1,357 @@ +""" +Struts Bolted to End Gusset - Design Adapter +Implements the business logic for compression member bolted design +""" +# Module identifier for CommonDesignLogic (used for CAD generation) +KEY_DISP_STRUT_BOLTED_END_GUSSET = "Struts Bolted to End Gusset" + +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl +) +from OCC.Core import BRepTools +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.design_type.compression_member.compression_bolted import Compression_bolted +import sys +import os +import typing +from typing import Dict, Any, List +import json +import traceback + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + """Return all required input parameters for the module.""" + return [ + "Member.Profile", # KEY_SEC_PROFILE + "Member.Designation", # KEY_SECSIZE + "Material", # KEY_MATERIAL + "Member.Material", # KEY_SEC_MATERIAL + "Connector.Plate.Thickness_List", # KEY_PLATETHK + "Bolt.Diameter", # KEY_D + "Bolt.Grade", # KEY_GRD + "Bolt.Type", # KEY_TYP + "Bolt.Bolt_Hole_Type", # KEY_DP_BOLT_HOLE_TYPE + "Bolt.Slip_Factor", # KEY_DP_BOLT_SLIP_FACTOR + "Connector.Material", # KEY_CONNECTOR_MATERIAL + "Design.Design_Method", # KEY_DP_DESIGN_METHOD + "Detailing.Corrosive_Influences", # KEY_DP_DETAILING_CORROSIVE_INFLUENCES + "Detailing.Edge_type", # KEY_DP_DETAILING_EDGE_TYPE + "Detailing.Gap", # KEY_DP_DETAILING_GAP + "Load.Axial", # KEY_AXIAL + "Member.Length", # KEY_LENGTH + "Conn_Location", # KEY_LOCATION + "Member.End_1", # KEY_END1 + "Member.End_2", # KEY_END2 + "Module" # KEY_MODULE + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + required_keys = get_required_keys() + missing_keys = contains_keys(input_values, required_keys) + if missing_keys is not None: + raise MissingKeyError(missing_keys[0]) + + # Validate Bolt.Bolt_Hole_Type + if not isinstance(input_values["Bolt.Bolt_Hole_Type"], str): + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type", "str") + + # Validate Bolt.Diameter + bolt_diameter = input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter, list) + or not validate_list_type(bolt_diameter, str) + or not custom_list_validation(bolt_diameter, int_able)): + raise InvalidInputTypeError("Bolt.Diameter", "non empty List[str] where all items can be converted to int") + + # Validate Bolt.Grade + bolt_grade = input_values["Bolt.Grade"] + if (not isinstance(bolt_grade, list) + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): + raise InvalidInputTypeError("Bolt.Grade", "non empty List[str] where all items can be converted to float") + + # Validate Bolt.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor, str) + or not float_able(bolt_slipfactor)): + raise InvalidInputTypeError("Bolt.Slip_Factor", "str where str can be converted to float") + + # Validate Bolt.Type + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") + + # Validate Connector.Material + if not isinstance(input_values["Connector.Material"], str): + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + if not isinstance(input_values["Design.Design_Method"], str): + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + raise InvalidInputTypeError("Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + if not isinstance(input_values["Detailing.Edge_type"], str): + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) + or not int_able(detailing_gap)): + raise InvalidInputTypeError("Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) + or not int_able(load_axial)): + raise InvalidInputTypeError("Load.Axial", "str where str can be converted to int") + + # Validate Material + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") + + # Validate Member.Profile + if not isinstance(input_values["Member.Profile"], str): + raise InvalidInputTypeError("Member.Profile", "str") + + # Validate Member.Designation + if not isinstance(input_values["Member.Designation"], str): + raise InvalidInputTypeError("Member.Designation", "str") + + # Validate Member.Length + if not isinstance(input_values["Member.Length"], str): + raise InvalidInputTypeError("Member.Length", "str") + + # Validate Conn_Location + if not isinstance(input_values["Conn_Location"], str): + raise InvalidInputTypeError("Conn_Location", "str") + + # Validate End Conditions + if not isinstance(input_values["Member.End_1"], str): + raise InvalidInputTypeError("Member.End_1", "str") + if not isinstance(input_values["Member.End_2"], str): + raise InvalidInputTypeError("Member.End_2", "str") + + # Validate Module + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") + + # Validate Connector.Plate.Thickness_List + connector_plate_thicknesslist = input_values["Connector.Plate.Thickness_List"] + if (not isinstance(connector_plate_thicknesslist, list) + or not validate_list_type(connector_plate_thicknesslist, str) + or not custom_list_validation(connector_plate_thicknesslist, int_able)): + raise InvalidInputTypeError("Connector.Plate.Thickness_List", "List[str] where all items can be converted to int") + + +def create_module() -> Compression_bolted: + """Create an instance of the Compression_bolted module design class and set it up for use""" + module = Compression_bolted() + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> Compression_bolted: + """Create an instance of the Compression_bolted module design class from input values.""" + module = create_module() + # Plate.Thickness expects a list, take the first value if present, else "" + if isinstance(input_values.get("Connector.Plate.Thickness_List", None), list) and input_values["Connector.Plate.Thickness_List"]: + input_values["Plate.Thickness"] = input_values["Connector.Plate.Thickness_List"][0] + else: + input_values["Plate.Thickness"] = "" + + module.set_input_values(input_values) + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate, format and return the output values from the given input values.""" + output = {} + module = create_from_input(input_values) + raw_output_text = module.output_values(True) + raw_output_spacing = module.spacing(True) + logs = module.logs + raw_output = raw_output_spacing + raw_output_text + for param in raw_output: + if param[2] == "TextBox": + key = param[0] + label = param[1] + value = param[3] + output[key] = { + "key": key, + "label": label, + "val": value + } + return output, logs + + +def setup_for_cad(cld, module): + """Setup CommonDesignLogic for CAD model generation.""" + cld.module = module.module + cld.mainmodule = module.mainmodule + cld.module_object = module + cld.call_3DModel(True, module) + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + if section not in ("Model", "Member", "Plate", "Endplate"): + raise InvalidInputTypeError("section", "'Model', 'Member', 'Plate', 'Endplate'") + + module = create_from_input(input_values) + + # Object that will create the CAD model. + try: + cld = CommonDesignLogic(None, None, '', KEY_DISP_STRUT_BOLTED_END_GUSSET, module.mainmodule) + except Exception as e: + print('error in cld e : ', e) + raise + + try: + # Setup the calculations object for generating CAD model. + setup_for_cad(cld, module) + except Exception as e: + traceback.print_exc() + print('Error in setting up cad e : ', e) + raise + + # The section of the module that will be generated. + cld.component = section + + # When section == "Model", also ensure per-part shapes exist and prepare a compound + part_names = ["Member", "Plate", "Endplate"] + part_files = {} + compound_model = None + + try: + if section == "Model": + # Build compound by adding each part shape without fusing + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + + for part in part_names: + try: + # Generate shape for this part + cld.component = part + part_shape = cld.create2Dcad() + if part_shape is None: + continue + + # Add to compound + builder.Add(compound, part_shape) + + # Ensure per-part BREP file exists (write or overwrite) + part_file_name = f"{session}_{part}.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + BRepTools.breptools.Write(part_shape, part_file_path_rel, Message_ProgressRange()) + part_files[part] = part_file_path_rel + # Also write STL for this part + try: + part_stl_rel = part_file_path_rel.replace(".brep", ".stl") + write_stl(part_shape, os.path.join(os.getcwd(), part_stl_rel)) + except Exception as stle: + print(f"Failed to write STL for part {part}: {stle}") + except Exception as e: + print(f"Failed to build/write part {part}: {e}") + + # Reset component to Model and set compound as the model to write + cld.component = section + compound_model = compound + # Generate model for non-Model sections (or fallback) + if compound_model is not None: + model = compound_model + else: + model = cld.create2Dcad() + except Exception as e: + print("Error in cld.create2Dcad() e : ", e) + raise + + # check if the cad_models folder exists or not + if(not os.path.exists(os.path.join(os.getcwd(), "file_storage/cad_models/"))): + print("path does not exists cad_models , creating one") + os.makedirs(os.path.join(os.getcwd(), "file_storage/cad_models/"), exist_ok=True) + + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) + + # If it's "Model" section, write a manifest referencing per-part breps and save extra formats + if section == "Model": + try: + manifest = { + "session": session, + "mergedBrep": file_path, + "parts": [ + {"name": name, "brepPath": part_files.get(name)} for name in part_names if part_files.get(name) + ] + } + # add stlPath for parts + for entry in manifest["parts"]: + if entry.get("brepPath"): + entry["stlPath"] = entry["brepPath"].replace(".brep", ".stl") + manifest_path = file_path.replace(".brep", ".parts.json") + full_manifest_path = os.path.join(os.getcwd(), manifest_path) + with open(full_manifest_path, "w", encoding="utf-8") as mf: + json.dump(manifest, mf) + print(f"Parts manifest saved at {full_manifest_path}") + except Exception as me: + print(f"Warning: Failed to write manifest: {me}") + + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + # Write merged STL for Model + try: + merged_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), merged_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), merged_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save merged STL: {stle}") + except Exception as e: + print('Writing to BREP file failed e : ', e) + raise + + # For non-Model sections, export single STL next to BREP + if section != "Model": + try: + single_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), single_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save STL for {section}: {stle}") + + return file_path diff --git a/backend/apps/modules/compression_member/submodules/struts_bolted/service.py b/backend/apps/modules/compression_member/submodules/struts_bolted/service.py new file mode 100644 index 000000000..97ed630cd --- /dev/null +++ b/backend/apps/modules/compression_member/submodules/struts_bolted/service.py @@ -0,0 +1,93 @@ +""" +Struts Bolted to End Gusset - Service Layer +Bridges between API and osdag_core +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class StrutsBoltedService: + """Service class for Struts-Bolted-Design module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """ + Run design calculation and return results. + + Args: + inputs: Dictionary of input parameters + request: Optional Django request object (for future use) + project_id: Optional project ID (for future use) + user_email: Optional user email (for future use) + + Returns: + Dictionary with 'data' (results) and 'logs' (calculation logs) + """ + print("=" * 60) + print("StrutsBoltedService.calculate() called") + print("=" * 60) + print(f"Inputs received: {list(inputs.keys())[:10]}...") # Print first 10 keys + + try: + # Validate inputs + print("\n[1/3] Validating inputs...") + validate_input(inputs) + print("✅ Input validation passed") + + # Generate formatted output (this handles module creation and calculation) + print("\n[2/3] Generating output (creates module and runs calculation)...") + output, logs = generate_output(inputs) + print(f"✅ Output generated: {len(output)} output parameters") + print(f"✅ Logs retrieved: {len(logs) if logs else 0} log entries") + + print("\n[3/3] Preparing response...") + result = { + 'data': output, + 'logs': logs or [], # Ensure logs is always a list + 'success': True + } + print("✅ Response prepared successfully") + print("=" * 60) + + return result + + except Exception as e: + print("\n" + "=" * 60) + print("❌ ERROR in StrutsBoltedService.calculate()") + print("=" * 60) + print(f"Exception type: {type(e).__name__}") + print(f"Exception message: {str(e)}") + + # Safely extract error message + error_msg = str(e) + if hasattr(e, 'error') and e.error is not None: + error_msg = str(e.error) + elif hasattr(e, 'args') and len(e.args) > 0: + error_msg = str(e.args[0]) + + print(f"Final error message: {error_msg}") + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60) + + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': error_msg + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate ('Model', 'Member', 'Plate', 'Endplate') + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) diff --git a/backend/apps/modules/compression_member/tmp_cad/12f533fc-74e0-4780-814e-c8f55a18db19/Model.brep b/backend/apps/modules/compression_member/tmp_cad/12f533fc-74e0-4780-814e-c8f55a18db19/Model.brep new file mode 100644 index 000000000..c3a8c0012 --- /dev/null +++ b/backend/apps/modules/compression_member/tmp_cad/12f533fc-74e0-4780-814e-c8f55a18db19/Model.brep @@ -0,0 +1 @@ +No CAD model produced for this input (empty). \ No newline at end of file diff --git a/backend/apps/modules/compression_member/tmp_cad/401bd20f-ba43-46c2-8cec-14ce9ac13fb2/Model.brep b/backend/apps/modules/compression_member/tmp_cad/401bd20f-ba43-46c2-8cec-14ce9ac13fb2/Model.brep new file mode 100644 index 000000000..c3a8c0012 --- /dev/null +++ b/backend/apps/modules/compression_member/tmp_cad/401bd20f-ba43-46c2-8cec-14ce9ac13fb2/Model.brep @@ -0,0 +1 @@ +No CAD model produced for this input (empty). \ No newline at end of file diff --git a/backend/apps/modules/compression_member/tmp_cad/95847d5d-1140-4fef-a762-cfac12dc8f5a/Model.brep b/backend/apps/modules/compression_member/tmp_cad/95847d5d-1140-4fef-a762-cfac12dc8f5a/Model.brep new file mode 100644 index 000000000..c3a8c0012 --- /dev/null +++ b/backend/apps/modules/compression_member/tmp_cad/95847d5d-1140-4fef-a762-cfac12dc8f5a/Model.brep @@ -0,0 +1 @@ +No CAD model produced for this input (empty). \ No newline at end of file diff --git a/backend/apps/modules/compression_member/tmp_cad/eaff29b8-10f2-47e4-a388-c3d3bac36084/Model.brep b/backend/apps/modules/compression_member/tmp_cad/eaff29b8-10f2-47e4-a388-c3d3bac36084/Model.brep new file mode 100644 index 000000000..c3a8c0012 --- /dev/null +++ b/backend/apps/modules/compression_member/tmp_cad/eaff29b8-10f2-47e4-a388-c3d3bac36084/Model.brep @@ -0,0 +1 @@ +No CAD model produced for this input (empty). \ No newline at end of file diff --git a/backend/apps/modules/compression_member/urls.py b/backend/apps/modules/compression_member/urls.py new file mode 100644 index 000000000..dc54c5d11 --- /dev/null +++ b/backend/apps/modules/compression_member/urls.py @@ -0,0 +1,11 @@ +""" +Compression Member URLs +""" +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import CompressionMemberViewSet + +router = DefaultRouter() +router.register(r'', CompressionMemberViewSet, basename='compression-member') + +urlpatterns = router.urls diff --git a/backend/apps/modules/compression_member/views.py b/backend/apps/modules/compression_member/views.py new file mode 100644 index 000000000..9a1927ff9 --- /dev/null +++ b/backend/apps/modules/compression_member/views.py @@ -0,0 +1,315 @@ +""" +Compression Member ViewSet - Routes to sub-module services +Uses URL slug (not POST body) to find the correct service +Handles guest mode and optional project_id saving +""" +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from .registry import CompressionMemberRegistry +from apps.core.utils.module_helpers import handle_design_request +from apps.core.utils.cad_helpers import generate_cad_models, get_default_sections +from apps.core.models import Material, CustomMaterials, Bolt, Angles, Channels, Beams, Columns, RHS, SHS, CHS + + +class CompressionMemberViewSet(viewsets.ViewSet): + """ + Generic ViewSet that routes to specific sub-module services based on URL slug. + Supports guest mode and optional project_id saving for authenticated users. + """ + permission_classes = [AllowAny] # Allow both authenticated and guest users + + @staticmethod + def _normalize_slug(raw_slug: str) -> str: + """ + Accept both URL slugs and MODULE_ID values from legacy/frontend calls. + Example inputs: + - 'struts_bolted' -> 'struts-bolted' (preferred) + - 'Struts-Bolted-Design' (legacy) + """ + if not raw_slug: + return raw_slug + # Convert underscores to hyphens (URL format to registry format) + normalized = raw_slug.replace('_', '-').lower() + module_id_map = { + 'struts-bolted-design': 'struts-bolted', + 'compression-member-design': 'struts-bolted', # Legacy support for old compression-member + 'axially-loaded-column': 'axially-loaded-column', + 'axially_loaded_column': 'axially-loaded-column', + 'axiallyloadedcolumn': 'axially-loaded-column', + } + return module_id_map.get(normalized, normalized) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/design') + def design(self, request, submodule_slug=None): + """ + POST /api/modules/compression-member/{submodule_slug}/design/ + + Request body: + { + "inputs": {...}, # Design input parameters + "project_id": 123 # Optional: Save results to project if user is authenticated + } + + Example: POST /api/modules/compression-member/struts_bolted/design/ + + Guest Mode: + - Can calculate designs + - Cannot save to projects (project_id is ignored) + + Authenticated Users: + - Can calculate designs + - Can save to projects if project_id is provided + """ + # Debug: trace registry lookup + print(f"[design] submodule_slug: {submodule_slug}") + normalized_slug = self._normalize_slug(submodule_slug) + print(f"[design] normalized_slug: {normalized_slug}") + print(f"[design] Registry keys: {list(CompressionMemberRegistry._registry.keys())}") + + # Use URL slug to find service (not POST body) + ServiceClass = CompressionMemberRegistry.get_service_by_slug(normalized_slug) + print(f"[design] ServiceClass: {ServiceClass}") + + if not ServiceClass: + return Response( + {'error': f'Sub-module {normalized_slug} not found'}, + status=404 + ) + + # Extract inputs and optional project_id + inputs = request.data.get('inputs', request.data) # Support both formats + project_id = request.data.get('project_id') + + # Handle authentication and project saving (shared logic) + context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug=normalized_slug, + module_name='compression-member' + ) + + try: + # Call the service with request context + result = ServiceClass.calculate( + inputs=inputs, + request=request, + project_id=project_id if not context['is_guest'] else None, + user_email=context['user_email'] + ) + + # Add project saving result to response + if context['project_result']: + result['project_saved'] = context['project_result']['saved'] + if context['project_result'].get('project_id'): + result['project_id'] = context['project_result']['project_id'] + if context['project_result'].get('error'): + result['project_error'] = context['project_result']['error'] + + return Response(result, status=200) + except Exception as e: + return Response( + {'error': str(e), 'success': False}, + status=400 + ) + + @action(detail=False, methods=['get'], url_path='(?P[^/.]+)/options') + def options(self, request, submodule_slug=None): + """ + GET /api/modules/compression-member/{submodule_slug}/options/ + + Returns input options for the sub-module (e.g., section lists, materials) + """ + email = request.query_params.get("email") + slug = self._normalize_slug(submodule_slug) + + # Shared helpers + def material_list(): + mats = list(Material.objects.all().values()) + if email: + mats += list(CustomMaterials.objects.filter(email=email).values()) + mats.append({"id": -1, "Grade": "Custom"}) + return mats + + def bolt_diameters(): + lst = list(Bolt.objects.values_list('Bolt_diameter', flat=True)) + lst.sort() + return [str(x) for x in lst] + + property_classes = ['3.6', '4.6', '4.8', '5.6', '5.8', '6.8', '8.8', '9.8', '10.9', '12.9'] + thickness_list = [ + '8', '10', '12', '14', '16', '18', '20', '22', '25', '28', '32', '36', '40', '45', '50', + '56', '63', '75', '80', '90', '100', '110', '120' + ] + section_profiles = ["Angles", "Back to Back Angles", "Star Angles", "Channels", "Back to Back Channels"] + bolt_hole_type_list = ["Standard", "Oversized", "Short Slotted", "Long Slotted"] + bolt_type_list = ["Bearing Bolt", "Friction Grip Bolt"] + bolt_slip_factor_list = ["0.2", "0.3", "0.48", "0.5"] + design_method_list = ["Limit State Design"] + edge_type_list = ["Rolled, machine-flame cut, sawn and planed"] + corrosive_influences_list = ["Yes", "No"] + end_condition_list = ["Fixed", "Hinged", "Free"] + load_type_list = ["Concentric Load", "Leg Load"] + conn_location_list = ["Long Leg", "Short Leg"] + + try: + if slug == 'struts-bolted': + data = { + 'materialList': material_list(), + 'connectorMaterialList': material_list(), + 'sectionProfileList': section_profiles, + 'angleList': list(Angles.objects.values_list('Designation', flat=True)), + 'channelList': list(Channels.objects.values_list('Designation', flat=True)), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + 'boltHoleTypeList': bolt_hole_type_list, + 'boltTypeList': bolt_type_list, + 'boltSlipFactorList': bolt_slip_factor_list, + 'designMethodList': design_method_list, + 'edgeTypeList': edge_type_list, + 'corrosiveInfluencesList': corrosive_influences_list, + 'endConditionList': end_condition_list, + 'loadTypeList': load_type_list, + 'connLocationList': conn_location_list, + } + return Response(data, status=status.HTTP_200_OK) + + if slug == 'axially-loaded-column': + section_profile_list = [ + "Beams and Columns", + "RHS and SHS", + "CHS", + "Angles", + "Back to Back Angles", + "Channels", + "Back to Back Channels", + ] + beams_list = list(Beams.objects.values_list("Designation", flat=True)) + columns_list = list(Columns.objects.values_list("Designation", flat=True)) + rhs_list = list(RHS.objects.values_list("Designation", flat=True)) + shs_list = list(SHS.objects.values_list("Designation", flat=True)) + chs_list = list(CHS.objects.values_list("Designation", flat=True)) + angles_list = list(Angles.objects.values_list("Designation", flat=True)) + channels_list = list(Channels.objects.values_list("Designation", flat=True)) + + end_condition_list = ["Fixed", "Free", "Hinged", "Roller"] + + data = { + "materialList": material_list(), # for Member/section material + "sectionProfileList": section_profile_list, + "beamList": [str(x) for x in beams_list], + "columnList": [str(x) for x in columns_list], + "rhsList": [str(x) for x in rhs_list], + "shsList": [str(x) for x in shs_list], + "chsList": [str(x) for x in chs_list], + "angleList": [str(x) for x in angles_list], + "channelList": [str(x) for x in channels_list], + "endConditionList": end_condition_list, + } + return Response(data, status=status.HTTP_200_OK) + + return Response({'error': f'Sub-module {slug} not found'}, status=status.HTTP_404_NOT_FOUND) + except Exception as exc: + return Response({'error': str(exc)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/cad') + def cad(self, request, submodule_slug=None): + """ + POST /api/modules/compression-member/{submodule_slug}/cad/ + + Request body: + { + "inputs": {...}, # Design input parameters + "sections": ["Model", "Member", ...] # Optional: specific sections to generate + } + + Returns: + { + "status": "success", + "files": {section: base64_data, ...}, + "hover_dict": {...}, + "warnings": [...] + } + """ + # Normalize slug + normalized_slug = self._normalize_slug(submodule_slug) + + # Get service from registry + ServiceClass = CompressionMemberRegistry.get_service_by_slug(normalized_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {normalized_slug} not found'}, + status=404 + ) + + # Extract inputs + inputs = request.data.get('inputs', request.data) + + if not inputs: + return Response( + {'error': 'inputs are required'}, + status=400 + ) + + # Get sections from request or use defaults + sections = request.data.get('sections') + if not sections: + sections = get_default_sections('compression-member', normalized_slug) + + if not sections: + return Response( + {'error': f'No sections defined for {normalized_slug}'}, + status=400 + ) + + try: + # Import adapter to get create_from_input function for hover_dict + create_from_input_func = None + try: + if normalized_slug == 'struts_bolted': + from .submodules.struts_bolted.adapter import create_from_input + create_from_input_func = create_from_input + elif normalized_slug == 'axially-loaded-column': + from .submodules.axially_loaded_column.adapter import create_from_input + create_from_input_func = create_from_input + except ImportError as e: + print(f"[CompressionMemberViewSet] Could not import create_from_input for {normalized_slug}: {e}") + + # Generate CAD models + result = generate_cad_models( + service_class=ServiceClass, + inputs=inputs, + sections=sections, + create_from_input_func=create_from_input_func + ) + + if not result['files']: + return Response( + { + 'status': 'error', + 'message': 'No CAD models were generated', + 'errors': result['warnings'] + }, + status=422 + ) + + return Response({ + 'status': 'success', + 'files': result['files'], + 'hover_dict': result['hover_dict'], + 'message': 'CAD models generated successfully', + 'warnings': result['warnings'] + }, status=201) + + except Exception as e: + print(f"[CompressionMemberViewSet] Error generating CAD: {e}") + import traceback + traceback.print_exc() + return Response( + {'error': str(e), 'status': 'error'}, + status=500 + ) diff --git a/backend/apps/modules/flexure_member/__init__.py b/backend/apps/modules/flexure_member/__init__.py new file mode 100644 index 000000000..16367194e --- /dev/null +++ b/backend/apps/modules/flexure_member/__init__.py @@ -0,0 +1,4 @@ +""" +Flexure Member Module +Parent module for flexural member design calculations (Simply Supported Beam) +""" diff --git a/backend/apps/modules/flexure_member/apps.py b/backend/apps/modules/flexure_member/apps.py new file mode 100644 index 000000000..e4f455eaf --- /dev/null +++ b/backend/apps/modules/flexure_member/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class FlexureMemberConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.modules.flexure_member' + diff --git a/backend/apps/modules/flexure_member/registry.py b/backend/apps/modules/flexure_member/registry.py new file mode 100644 index 000000000..8757756dc --- /dev/null +++ b/backend/apps/modules/flexure_member/registry.py @@ -0,0 +1,18 @@ +""" +Flexure Member Registry - Auto-discovers sub-modules +Inherits from BaseModuleRegistry (DRY principle) +""" +import os +from apps.core.registry import BaseModuleRegistry + + +class FlexureMemberRegistry(BaseModuleRegistry): + """Registry for flexure member sub-modules""" + pass + + +# Auto-discover sub-modules +_package_name = 'apps.modules.flexure_member.submodules' +_package_path = os.path.join(os.path.dirname(__file__), 'submodules') +FlexureMemberRegistry.auto_discover(_package_name, _package_path) + diff --git a/backend/apps/modules/flexure_member/submodules/__init__.py b/backend/apps/modules/flexure_member/submodules/__init__.py new file mode 100644 index 000000000..6ce145d94 --- /dev/null +++ b/backend/apps/modules/flexure_member/submodules/__init__.py @@ -0,0 +1,4 @@ +""" +Flexure Member Sub-modules +""" + diff --git a/backend/apps/modules/flexure_member/submodules/simply_supported_beam/__init__.py b/backend/apps/modules/flexure_member/submodules/simply_supported_beam/__init__.py new file mode 100644 index 000000000..0b7f72a60 --- /dev/null +++ b/backend/apps/modules/flexure_member/submodules/simply_supported_beam/__init__.py @@ -0,0 +1,6 @@ +""" +Simply Supported Beam Sub-module +""" +MODULE_ID = "Simply-Supported-Beam" +from .service import SimplySupportedBeamService as Service + diff --git a/backend/apps/modules/flexure_member/submodules/simply_supported_beam/adapter.py b/backend/apps/modules/flexure_member/submodules/simply_supported_beam/adapter.py new file mode 100644 index 000000000..cb706870f --- /dev/null +++ b/backend/apps/modules/flexure_member/submodules/simply_supported_beam/adapter.py @@ -0,0 +1,137 @@ +""" +Simply Supported Beam Adapter +Implements the business logic directly (not re-exporting from osdag_api) +""" +from osdag_core.Common import KEY_DISP_FLEXURE +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type +) +from OCC.Core import BRepTools +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.design_type.flexural_member.flexure import Flexure +import sys +import os +from typing import Dict, Any, List +import json + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + """Return all required input parameters for the module.""" + return [ + "Module", # KEY_MODULE + "Member.Profile", # KEY_SEC_PROFILE + "Member.Designation", # KEY_SECSIZE + "Material", # KEY_MATERIAL + "Member.Material", # KEY_SEC_MATERIAL + "Design.Design_Method", # KEY_DESIGN_TYPE_FLEXURE + "Design.Allowable_Class", # KEY_ALLOW_CLASS + "Design.Effective_Area_Parameter", # KEY_EFFECTIVE_AREA_PARA + "Length.Overwrite", # KEY_LENGTH_OVERWRITE + "Bearing.Length", # KEY_BEARING_LENGTH + "Load.Shear", # KEY_SHEAR + "Load.Moment", # KEY_MOMENT + "Member.Length", # KEY_LENGTH + "Support.Type", # KEY_SUPPORT + "Torsion.restraint", # KEY_TORSIONAL_RES + "Warping.restraint", # KEY_WARPING_RES + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + required_keys = get_required_keys() + missing_keys = contains_keys(input_values, required_keys) + if missing_keys is not None: + raise MissingKeyError(missing_keys[0]) + + +def create_module() -> Flexure: + """Create an instance of the Flexure module design class and set it up for use""" + module = Flexure() + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> Flexure: + """Create an instance of the Flexure module design class from input values.""" + module = create_module() + + # Handle array conversion for Member.Designation + member_designation = input_values.get('Member.Designation', '') + if isinstance(member_designation, str): + if member_designation == '[]': + member_designation = [] + elif member_designation.startswith('[') and member_designation.endswith(']'): + # Try to parse as JSON array + try: + member_designation = json.loads(member_designation) + except: + member_designation = [member_designation] # Fallback to single item array + elif not isinstance(member_designation, list): + member_designation = [str(member_designation)] + + # Validate that section designations are provided + if not member_designation or member_designation == []: + print(f"ERROR: No section designations provided! Frontend should populate this from beamList/columnList.") + member_designation = [] + + # Map input_values to exact keys expected by Flexure.set_input_values + design_dict = { + 'Module': input_values.get('Module', 'Simply-Supported-Beam'), + 'Member.Profile': input_values.get('Member.Profile', ''), + 'Member.Designation': member_designation, # Use processed array + 'Material': input_values.get('Material', ''), + 'Member.Material': input_values.get('Member.Material', ''), + 'Flexure.Type': input_values.get('Flexure.Type', 'Major Laterally Supported'), + 'Design.Allowable_Class': input_values.get('Design.Allowable_Class', ''), + 'Design.Effective_Area_Parameter': input_values.get('Design.Effective_Area_Parameter', ''), + 'Length.Overwrite': input_values.get('Length.Overwrite', ''), + 'Bearing.Length': input_values.get('Bearing.Length', ''), + 'Load.Shear': input_values.get('Load.Shear', ''), + 'Load.Moment': input_values.get('Load.Moment', ''), + 'Member.Length': input_values.get('Member.Length', ''), + 'Flexure.Support': input_values.get('Flexure.Support', ''), + 'Torsion.restraint': input_values.get('Torsion.restraint', ''), + 'Warping.restraint': input_values.get('Warping.restraint', ''), + 'Loading.Condition': 'Normal', # Default loading condition for simply supported beams + } + + module.set_input_values(design_dict) + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate, format and return the output values from the given input values.""" + module = create_from_input(input_values) + output = {} + logs = getattr(module, 'logs', []) if hasattr(module, 'logs') else [] + raw_output_text = module.output_values(True) + raw_output_spacing = module.spacing(True) + raw_output = raw_output_spacing + raw_output_text + for param in raw_output: + if len(param) > 2 and param[2] == "TextBox": + key = param[0] + label = param[1] + value = param[3] + output[key] = { + "key": key, + "label": label, + "val": value + } + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + # CAD generation not yet implemented for Simply Supported Beam + # Return empty string as per osdag_api implementation + return "" diff --git a/backend/apps/modules/flexure_member/submodules/simply_supported_beam/service.py b/backend/apps/modules/flexure_member/submodules/simply_supported_beam/service.py new file mode 100644 index 000000000..38fea60e6 --- /dev/null +++ b/backend/apps/modules/flexure_member/submodules/simply_supported_beam/service.py @@ -0,0 +1,94 @@ +""" +Simply Supported Beam Service - Business logic layer +Bridges between API and osdag_core +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class SimplySupportedBeamService: + """Service class for Simply-Supported-Beam module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """ + Run design calculation and return results. + + Args: + inputs: Dictionary of input parameters + request: Optional Django request object (for future use) + project_id: Optional project ID (for future use) + user_email: Optional user email (for future use) + + Returns: + Dictionary with 'data' (results) and 'logs' (calculation logs) + """ + print("=" * 60) + print("SimplySupportedBeamService.calculate() called") + print("=" * 60) + print(f"Inputs received: {list(inputs.keys())[:10]}...") # Print first 10 keys + + try: + # Validate inputs + print("\n[1/3] Validating inputs...") + validate_input(inputs) + print("✅ Input validation passed") + + # Generate formatted output (this handles module creation and calculation) + print("\n[2/3] Generating output (creates module and runs calculation)...") + output, logs = generate_output(inputs) + print(f"✅ Output generated: {len(output)} output parameters") + print(f"✅ Logs retrieved: {len(logs) if logs else 0} log entries") + + print("\n[3/3] Preparing response...") + result = { + 'data': output, + 'logs': logs or [], # Ensure logs is always a list + 'success': True + } + print("✅ Response prepared successfully") + print("=" * 60) + + return result + + except Exception as e: + print("\n" + "=" * 60) + print("ERROR in SimplySupportedBeamService.calculate()") + print("=" * 60) + print(f"Exception type: {type(e).__name__}") + print(f"Exception message: {str(e)}") + + # Safely extract error message + error_msg = str(e) + if hasattr(e, 'error') and e.error is not None: + error_msg = str(e.error) + elif hasattr(e, 'args') and len(e.args) > 0: + error_msg = str(e.args[0]) + + print(f"Final error message: {error_msg}") + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60) + + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': error_msg + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate (if applicable) + session: Session identifier for file naming + + Returns: + File path to the generated CAD model (or empty string if not implemented) + """ + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/flexure_member/urls.py b/backend/apps/modules/flexure_member/urls.py new file mode 100644 index 000000000..6987779e3 --- /dev/null +++ b/backend/apps/modules/flexure_member/urls.py @@ -0,0 +1,12 @@ +""" +Flexure Member URLs +""" +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import FlexureMemberViewSet + +router = DefaultRouter() +router.register(r'', FlexureMemberViewSet, basename='flexure-member') + +urlpatterns = router.urls + diff --git a/backend/apps/modules/flexure_member/views.py b/backend/apps/modules/flexure_member/views.py new file mode 100644 index 000000000..9fe1cd8a6 --- /dev/null +++ b/backend/apps/modules/flexure_member/views.py @@ -0,0 +1,191 @@ +""" +Flexure Member ViewSet - Routes to sub-module services +Uses URL slug (not POST body) to find the correct service +Handles guest mode and optional project_id saving +""" +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from .registry import FlexureMemberRegistry +from apps.core.utils.module_helpers import handle_design_request +from apps.core.utils.cad_helpers import generate_cad_models, get_default_sections + + +class FlexureMemberViewSet(viewsets.ViewSet): + """ + Generic ViewSet that routes to specific sub-module services based on URL slug. + Supports guest mode and optional project_id saving for authenticated users. + """ + permission_classes = [AllowAny] # Allow both authenticated and guest users + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/design') + def design(self, request, submodule_slug=None): + """ + POST /api/modules/flexure-member/{submodule_slug}/design/ + + Request body: + { + "inputs": {...}, # Design input parameters + "project_id": 123 # Optional: Save results to project if user is authenticated + } + + Example: POST /api/modules/flexure-member/simply-supported-beam/design/ + + Guest Mode: + - Can calculate designs + - Cannot save to projects (project_id is ignored) + + Authenticated Users: + - Can calculate designs + - Can save to projects if project_id is provided + """ + # Use URL slug to find service (not POST body) + ServiceClass = FlexureMemberRegistry.get_service_by_slug(submodule_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {submodule_slug} not found'}, + status=404 + ) + + # Extract inputs and optional project_id + inputs = request.data.get('inputs', request.data) # Support both formats + project_id = request.data.get('project_id') + + # Handle authentication and project saving (shared logic) + context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug=submodule_slug, + module_name='flexure-member' + ) + + try: + # Call the service with request context + result = ServiceClass.calculate( + inputs=inputs, + request=request, + project_id=project_id if not context['is_guest'] else None, + user_email=context['user_email'] + ) + + # Add project saving result to response + if context['project_result']: + result['project_saved'] = context['project_result']['saved'] + if context['project_result'].get('project_id'): + result['project_id'] = context['project_result']['project_id'] + if context['project_result'].get('error'): + result['project_error'] = context['project_result']['error'] + + return Response(result, status=200) + except Exception as e: + return Response( + {'error': str(e), 'success': False}, + status=400 + ) + + @action(detail=False, methods=['get'], url_path='(?P[^/.]+)/options') + def options(self, request, submodule_slug=None): + """ + GET /api/modules/flexure-member/{submodule_slug}/options/ + + Returns input options for the sub-module (e.g., section lists, materials) + """ + # TODO: Implement options endpoint if needed + return Response({'message': 'Options endpoint not yet implemented'}, status=501) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/cad') + def cad(self, request, submodule_slug=None): + """ + POST /api/modules/flexure-member/{submodule_slug}/cad/ + + Request body: + { + "inputs": {...}, # Design input parameters + "sections": ["Model", ...] # Optional: specific sections to generate + } + + Returns: + { + "status": "success", + "files": {section: base64_data, ...}, + "hover_dict": {...}, + "warnings": [...] + } + """ + # Get service from registry + ServiceClass = FlexureMemberRegistry.get_service_by_slug(submodule_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {submodule_slug} not found'}, + status=404 + ) + + # Extract inputs + inputs = request.data.get('inputs', request.data) + + if not inputs: + return Response( + {'error': 'inputs are required'}, + status=400 + ) + + # Get sections from request or use defaults + sections = request.data.get('sections') + if not sections: + sections = get_default_sections('flexure-member', submodule_slug) + + if not sections: + return Response( + {'error': f'No sections defined for {submodule_slug}'}, + status=400 + ) + + try: + # Import adapter to get create_from_input function for hover_dict + create_from_input_func = None + try: + if submodule_slug == 'simply-supported-beam': + from .submodules.simply_supported_beam.adapter import create_from_input + create_from_input_func = create_from_input + except ImportError as e: + print(f"[FlexureMemberViewSet] Could not import create_from_input for {submodule_slug}: {e}") + + # Generate CAD models + result = generate_cad_models( + service_class=ServiceClass, + inputs=inputs, + sections=sections, + create_from_input_func=create_from_input_func + ) + + if not result['files']: + return Response( + { + 'status': 'error', + 'message': 'No CAD models were generated', + 'errors': result['warnings'] + }, + status=422 + ) + + return Response({ + 'status': 'success', + 'files': result['files'], + 'hover_dict': result['hover_dict'], + 'message': 'CAD models generated successfully', + 'warnings': result['warnings'] + }, status=201) + + except Exception as e: + print(f"[FlexureMemberViewSet] Error generating CAD: {e}") + import traceback + traceback.print_exc() + return Response( + {'error': str(e), 'status': 'error'}, + status=500 + ) + diff --git a/backend/apps/modules/moment_connection/__init__.py b/backend/apps/modules/moment_connection/__init__.py new file mode 100644 index 000000000..8580bd09c --- /dev/null +++ b/backend/apps/modules/moment_connection/__init__.py @@ -0,0 +1,2 @@ +# Moment Connection module package + diff --git a/backend/apps/modules/moment_connection/apps.py b/backend/apps/modules/moment_connection/apps.py new file mode 100644 index 000000000..ff8c1e20b --- /dev/null +++ b/backend/apps/modules/moment_connection/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class MomentConnectionConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.modules.moment_connection' + diff --git a/backend/apps/modules/moment_connection/registry.py b/backend/apps/modules/moment_connection/registry.py new file mode 100644 index 000000000..8cff3256e --- /dev/null +++ b/backend/apps/modules/moment_connection/registry.py @@ -0,0 +1,18 @@ +""" +Moment Connection Registry - Auto-discovers sub-modules +Inherits from BaseModuleRegistry (DRY principle) +""" +import os +from apps.core.registry import BaseModuleRegistry + + +class MomentConnectionRegistry(BaseModuleRegistry): + """Registry for moment connection sub-modules""" + pass + + +# Auto-discover sub-modules +_package_name = 'apps.modules.moment_connection.submodules' +_package_path = os.path.join(os.path.dirname(__file__), 'submodules') +MomentConnectionRegistry.auto_discover(_package_name, _package_path) + diff --git a/backend/apps/modules/moment_connection/shared.py b/backend/apps/modules/moment_connection/shared.py new file mode 100644 index 000000000..de7f1f55c --- /dev/null +++ b/backend/apps/modules/moment_connection/shared.py @@ -0,0 +1,55 @@ +""" +Shared utilities for moment connection modules +""" +from osdag_core.cad.common_logic import CommonDesignLogic +from OCC.Display.backend import * +from osdag_core.Common import * + + +def setup_for_cad(cdl: CommonDesignLogic, module_class): + """Sets up the CommonLogicObject before generating CAD""" + print('SETTING UP FOR CAD', module_class) + cdl.module_class = module_class # Set the module class in design logic object. + cdl.module_object = module_class # Set the module object (required by common_logic.py) + print("******") + module_object = module_class + print("********") + # Ensure CommonDesignLogic has module/mainmodule for moment connections + if not getattr(cdl, "mainmodule", None): + cdl.mainmodule = getattr(module_object, "mainmodule", None) + + # Column cover plate (bolted / welded) and column end plate support + if module_object.module in ( + "ColumnCoverPlateBolted", + "Column-to-Column Cover Plate Bolted Connection", + "Column-to-Column-Cover-Plate-Bolted-Connection", + ): + cdl.C = module_object + cdl.CPObj = cdl.createCCCoverPlateCAD() + if module_object.module in ( + "Column-to-Column Cover Plate Welded Connection", + "Column-to-Column-Cover-Plate-Welded-Connection", + ): + cdl.C = module_object + cdl.CPObj = cdl.createCCCoverPlateCAD() + if module_object.module in ( + "Column-to-Column-End-Plate-Connection", + "Column-to-Column End Plate Connection", + KEY_DISP_COLUMNENDPLATE, + ): + cdl.CEP = module_object + cdl.CEPObj = cdl.createCCEndPlateCAD() + + # Beam-side moment modules + if module_object.module == "Beam-to-Beam Cover Plate Bolted Connection": + # Required for createBBCoverPlateCAD which expects self.B to exist + cdl.B = module_object + cdl.CPObj = cdl.createBBCoverPlateCAD() + if module_object.module == "Beam-to-Beam End Plate Connection": + cdl.CPObj = cdl.createBBEndPlateCAD() + if module_object.module == "Beam-to-Beam Cover Plate Welded Connection": + cdl.B = module_object + cdl.CPObj = cdl.createBBCoverPlateCAD() + if module_object.module == "Beam-to-Column End Plate Connection": + cdl.CPObj = cdl.createBCEndPlateCAD() # Initialize the CAD object for beam-column end plate + diff --git a/cad/BBCad/__init__.py b/backend/apps/modules/moment_connection/submodules/__init__.py similarity index 100% rename from cad/BBCad/__init__.py rename to backend/apps/modules/moment_connection/submodules/__init__.py diff --git a/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_bolted/__init__.py b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_bolted/__init__.py new file mode 100644 index 000000000..8649d2a15 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_bolted/__init__.py @@ -0,0 +1,6 @@ +""" +Cover Plate Bolted Connection Sub-module +""" +MODULE_ID = 'Cover-Plate-Bolted-Connection' +from .service import CoverPlateBoltedService as Service + diff --git a/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_bolted/adapter.py b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_bolted/adapter.py new file mode 100644 index 000000000..e8ec11b25 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_bolted/adapter.py @@ -0,0 +1,475 @@ +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl, +) +from ...shared import setup_for_cad # Use moment_connection shared utilities +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from osdag_core.cad.common_logic import CommonDesignLogic +# Will log a lot of unnessecary data. +from osdag_core.design_type.connection.beam_cover_plate import BeamCoverPlate +import sys +import os +from typing import Dict, Any, List +import traceback + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + return [ + "Bolt.Bolt_Hole_Type", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Slip_Factor", + "Bolt.TensionType", + "Bolt.Type", + "Connector.Flange_Plate.Preferences", + "Connector.Flange_Plate.Thickness_list", + "Connector.Material", + "Connector.Web_Plate.Thickness_List", + "Design.Design_Method", + "Detailing.Corrosive_Influences", + "Detailing.Edge_type", + "Detailing.Gap", + "Load.Axial", + "Load.Moment", + "Load.Shear", + "Material", + "Member.Designation", + "Member.Material", + "Module", + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + raise MissingKeyError(missing_keys[0]) + + # Validate key types one by one: + + # Validate Bolt.Bolt_Hole_Type. + # Check if Bolt.Bolt_Hole_Type is a string. + if not isinstance(input_values["Bolt.Bolt_Hole_Type"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type", "str") + + # Validate Bolt.Diameter. + bolt_diameter = input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter, list) # Check if Bolt.Diameter is a list. + # Check if all items in Bolt.Diameter are str. + or not validate_list_type(bolt_diameter, str) + or not custom_list_validation(bolt_diameter, int_able)): # Check if all items in Bolt.Diameter can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Diameter", "non empty List[str] where all items can be converted to int") + + # Validate Bolt.Grade + bolt_grade = input_values["Bolt.Grade"] + if (not isinstance(bolt_grade, list) # Check if Bolt.Grade is a list. + # Check if all items in Bolt.Grade are str. + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): # Check if all items in Bolt.Grade can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Grade", "non empty List[str] where all items can be converted to float") + + # Validate Bolt.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor, str) # Check if Bolt.Slip_Factor is a string. + or not float_able(bolt_slipfactor)): # Check if Bolt.Slip_Factor can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Slip_Factor", "str where str can be converted to float") + + # Validate Bolt.TensionType + # Check if Bolt.TensionType is a string. + if not isinstance(input_values["Bolt.TensionType"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.TensionType", "str") + + # Validate Bolt.Type + # Check if Bolt.Type is a string. + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") # If not, raise error. + + # Validate Connector.Flange_Plate.Preferences + if not isinstance(input_values["Connector.Flange_Plate.Preferences"], str): + raise InvalidInputTypeError("Connector.Flange_Plate.Preferences", "str") + + # Validate Connector.Flange_Plate.Thickness_list + flange_thickness_list = input_values["Connector.Flange_Plate.Thickness_list"] + if (not isinstance(flange_thickness_list, list) + or not validate_list_type(flange_thickness_list, str) + or not custom_list_validation(flange_thickness_list, int_able)): + raise InvalidInputTypeError( + "Connector.Flange_Plate.Thickness_list", "List[str] where all items can be converted to int") + # Validate Connector.Material + # Check if Connector.Material is a string. + if not isinstance(input_values["Connector.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Connector.Web_Plate.Thickness_List + web_thickness_list = input_values["Connector.Web_Plate.Thickness_List"] + if (not isinstance(web_thickness_list, list) + or not validate_list_type(web_thickness_list, str) + or not custom_list_validation(web_thickness_list, int_able)): + raise InvalidInputTypeError( + "Connector.Web_Plate.Thickness_List", "List[str] where all items can be converted to int") + + # Validate Design.Design_Method + # Check if Design.Design_Method is a string. + if not isinstance(input_values["Design.Design_Method"], str): + # If not, raise error. + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + # Check if Detailing.Corrosive_Influences is 'Yes' or 'No'. + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + # If not, raise error. + raise InvalidInputTypeError( + "Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + # Check if Detailing.Edge_type is a string. + if not isinstance(input_values["Detailing.Edge_type"], str): + # If not, raise error. + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) # Check if Detailing.Gap is a string. + or not int_able(detailing_gap)): # Check if Detailing.Gap can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) # Check if Load.Axial is a string. + or not int_able(load_axial)): # Check if Load.Axial can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Axial", "str where str can be converted to int") + + # Validate Load.Moment + load_moment = input_values["Load.Moment"] + if not isinstance(load_moment, str) or not int_able(load_moment): + raise InvalidInputTypeError("Load.Moment", "str where str can be converted to int") + + # Validate Load.Shear + load_shear = input_values["Load.Shear"] + if (not isinstance(load_shear, str) # Check if Load.Shear is a string. + or not int_able(load_shear)): # Check if Load.Shear can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Shear", "str where str can be converted to int") + + # Validate Material + # Check if Material is a string. + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") # If not, raise error. + + # Validate Member.Designation + if not isinstance(input_values["Member.Designation"], str): + raise InvalidInputTypeError("Member.Designation", "str") + + # Validate Member.Material + if not isinstance(input_values["Member.Material"], str): + raise InvalidInputTypeError("Member.Material", "str") + + # Check if Module is a string. + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") # If not, raise error. + +def validate_input_new(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + print('required_keys : ' , required_keys) + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + print('missing keys : ' , missing_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + print("missing keys is not None") + raise MissingKeyError(missing_keys[0]) + + # Validate key types using loops. + + # Validate all strings. + str_keys = ["Bolt.Bolt_Hole_Type", # List of all parameters that are strings + "Bolt.TensionType", + "Bolt.Type", + "Bolt.Connectivity", + "Bolt.Connector_Material", + "Connector.Flange_Plate.Preferences", + "Design.Design_Method", + "Detailing.Edge_type", + "Material", + "Member.Designation", + "Member.Material", + "Module" + ] + for key in str_keys: # Loop through all keys. + print('validating string key') + + try : + validate_string(key) # Check if key is a string. If not, raise error. + except : + print('error in validating string keys') + print('string key passed : ' , key ) + + # Validate for keys that are numbers + num_keys = [("Bolt.Slip_Factor", True), # List of all parameters that are numbers (key, is_float) + ("Detailing.Gap", False), + ("Load.Axial", False), + ("Load.Moment", False), + ("Load.Shear", False), + ] + for key in num_keys: # Loop through all keys. + # Check if key is a number. If not, raise error. + print('validating num keys') + validate_num(key[0], key[1]) + + # Validate for keys that are arrays + arr_keys = [("Bolt.Diameter", False), # List of all parameters that can be converted to numbers (key, is_float) + ("Bolt.Grade", True), + ("Connector.Flange_Plate.Thickness_List", False), + ("Connector.Web_Plate.Thicness_List", False)] + for key in arr_keys: + print('validating arr key') + # Check if key is a list where all items can be converted to numbers. If not, raise error. + validate_arr(key[0], key[1]) + + +def create_module() -> BeamCoverPlate: + """Create an instance of the FinPlateConnection module design class and set it up for use""" + module = BeamCoverPlate() # Create an instance of the FinPlateConnection + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> BeamCoverPlate: + """Create an instance of the FinPlateConnection module design class from input values.""" + # validate_input(input_values) + try : + module = create_module() # Create module instance. + except Exception as e : + print('e in create_module : ' , e) + print('error in creating module') + + # Set the input values on the module instance. + try : + print("INPUT SET FOR FINAL OUTPUT",input_values) + module.set_input_values(input_values) + except Exception as e : + traceback.print_exc() + print('e in set_input_values : ' , e) + print('error in setting the input values') + + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """ + Generate, format and return the input values from the given output values. + Output format (json): { + "Bolt.Pitch": + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)" + "value": 40 + } + } + """ + print("************") + output = {} # Dictionary for formatted values + module = create_from_input(input_values) # Create module from input. + print('module : ' , module) + print('type of module : ' , type(module)) + + # Generate output values in unformatted form. + raw_output_text = module.output_values(True) + raw_member_capacity = module.member_capacityoutput(True) + raw_flange_bolt_capacity = [ + (f"{key}_flange_bolt_capacity", label, typ, value, visible if len(item) == 5 else True) + for item in module.flange_bolt_capacity(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_web_bolt_capacity = [ + (f"{key}_web_bolt_capacity", label, typ, value, visible if len(item) == 5 else True) + for item in module.web_bolt_capacity(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_flange_capacity = [ + (f"{key}_flange_capacity", label, typ, value, visible if len(item) == 5 else True) + for item in module.flangecapacity(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_web_capacity = [ + (f"{key}_web_capacity", label, typ, value, visible if len(item) == 5 else True) + for item in module.webcapacity(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_flange_spacing = [ + (f"{key}_flange_spacing", label, typ, value, visible if len(item) == 5 else True) + for item in module.flangespacing(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_web_spacing = [ + (f"{key}_web_spacing", label, typ, value, visible if len(item) == 5 else True) + for item in module.webspacing(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + from osdag_core.custom_logger import CustomLogger + logs = [] + if hasattr(module, "logger") and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() or [] + else: + logs = getattr(module, "logs", []) or [] + raw_output = ( + raw_output_text + + raw_member_capacity + + raw_flange_bolt_capacity + + raw_web_bolt_capacity + + raw_flange_capacity + + raw_web_capacity + + raw_flange_spacing + + raw_web_spacing + ) + # os.system("clear") + # Loop over all the text values and add them to ouptut dict. + for param in raw_output: + if param[2] == "TextBox": # If the parameter is a text output, + key = param[0] # id/key + label = param[1] # label text. + value = param[3] # Value as string. + output[key] = { + "key": key, + "label": label, + "val": value # Changed from "value" to "val" to match frontend expectations + } # Set label, key and value in output + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP/STL file. + + External API uses section names: "Model", "Beam", "CoverPlate". + Internally, the legacy CAD logic for beam cover plate uses component name "Connector" + for the cover plate + bolts assembly. Map CoverPlate -> Connector for CAD routing, + but keep the external section name for file naming and response keys. + """ + if section not in ("Model", "Beam", "CoverPlate"): + raise InvalidInputTypeError("section", "'Model', 'Beam' or 'CoverPlate'") + + module = create_from_input(input_values) + + # Ensure correct display keys for CAD routing + from osdag_core.Common import KEY_DISP_BEAMCOVERPLATE + if getattr(module, "module", None) != KEY_DISP_BEAMCOVERPLATE: + module.module = KEY_DISP_BEAMCOVERPLATE + module.mainmodule = "Moment Connection" + + # Build CommonDesignLogic (display, cad_widget, folder, connection, mainmodule) + print(f"[CAD DEBUG] BB cover plate bolted: module={module.module}, mainmodule={module.mainmodule}, section={section}") + cld = CommonDesignLogic(None, "", "", module.module, module.mainmodule) + + # Setup module for CAD + setup_for_cad(cld, module) + + # Map external section names to internal component names expected by CommonDesignLogic + internal_section = section + if section == "CoverPlate": + # Legacy CAD uses component name "Connector" for cover plate + bolts + internal_section = "Connector" + + cld.component = internal_section + print(f"[cadissue] BB cover plate bolted: cld.component set to {internal_section} for section={section}") + model = cld.create2Dcad() + + # check if the cad_models folder exists or not + # if no, then create one + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + + print('2d model : ' , model) + # os.system("clear") # clear the terminal + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + print('brep file path in create_cad_model : ' , file_path) + + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) # Generate CAD Model + + # Always try to write STL for the requested section + try: + stl_rel = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_rel) + write_stl(model, full_stl) + print(f"STL file saved at {full_stl}") + except Exception as stle: + print(f"Warning: Failed to save STL at {file_path}: {stle}") + + if section == "Model": + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + + except Exception as e: + print('Writing to BREP/STL file failed e : ', e) + + return file_path + + diff --git a/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_bolted/service.py b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_bolted/service.py new file mode 100644 index 000000000..849e66963 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_bolted/service.py @@ -0,0 +1,31 @@ +""" +Cover Plate Bolted Service - Business logic layer +Bridges between API and osdag_core +""" +from osdag_core.design_type.connection.beam_cover_plate import BeamCoverPlate +from .adapter import validate_input, generate_output, create_cad_model + + +class CoverPlateBoltedService: + """Service class for Cover Plate Bolted Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + validate_input(inputs) + model = BeamCoverPlate() + if hasattr(model, "set_osdaglogger"): + model.set_osdaglogger(None, id="web") + model.set_input_values(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs, + 'success': True + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_welded/__init__.py b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_welded/__init__.py new file mode 100644 index 000000000..49d3af1d0 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_welded/__init__.py @@ -0,0 +1,6 @@ +""" +Cover Plate Welded Connection Sub-module +""" +MODULE_ID = 'Cover-Plate-Welded-Connection' +from .service import CoverPlateWeldedService as Service + diff --git a/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_welded/adapter.py b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_welded/adapter.py new file mode 100644 index 000000000..f4e961fa3 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_welded/adapter.py @@ -0,0 +1,332 @@ +""" +Api for Cover Plate Welded Connection module +Functions: + get_required_keys() -> List[str]: + Return all required input parameters for the module. + validate_input(input_values: Dict[str, Any]) -> None: + Go through all the input parameters. + Check if all required parameters are given. + Check if all parameters are of correct data type. + create_module() -> BeamCoverPlateWeld: + Create an instance of the cover plate welded connection module design class and set it up for use + create_from_input(input_values: Dict[str, Any]) -> BeamCoverPlateWeld + Create an instance of the cover plate welded connection module design class from input values. + generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + Generate, format and return the output values from the given input values. + create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + Generate the CAD model from input values as a BREP file. Return file path. +""" + +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl, +) + +from ...shared import setup_for_cad # Use moment_connection shared utilities +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.design_type.connection.beam_cover_plate_weld import BeamCoverPlateWeld +import sys +import os +import typing +from typing import Dict, Any, List +import traceback + +old_stdout = sys.stdout +sys.stdout = open(os.devnull, "w") +sys.stdout = old_stdout + + +def get_required_keys() -> List[str]: + return [ + "Module", + "Member.Designation", + "Member.Material", + "Material", + "Load.Moment", + "Load.Shear", + "Load.Axial", + "Weld.Type", + "Weld.Fab", + "Weld.Material_Grade_OverWrite", + "Design.Design_Method", + "Detailing.Gap", + "Connector.Material", + "Connector.Flange_Plate.Preferences", + "Connector.Flange_Plate.Thickness_list", + "Connector.Web_Plate.Thickness_List" + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + try: + required_keys = get_required_keys() + + # Filter out UI-specific keys that are not required by the backend module + filtered_input = {k: v for k, v in input_values.items() if k != "out_titles_status"} + + missing_keys = contains_keys(filtered_input, required_keys) + if missing_keys != None: + raise MissingKeyError(f"Required key '{missing_keys[0]}' is missing from input") + + # Validate string fields + string_fields = [ + "Module", + "Member.Designation", + "Member.Material", + "Material", + "Weld.Type", + "Weld.Fab", + "Design.Design_Method", + "Connector.Material", + "Connector.Flange_Plate.Preferences" + ] + for key in string_fields: + if key in filtered_input and not isinstance(filtered_input[key], str): + raise InvalidInputTypeError( + f"Field '{key}' must be a string, got {type(filtered_input[key]).__name__}" + ) + + # Validate numeric fields + numeric_fields = [ + "Load.Moment", + "Load.Shear", + "Load.Axial", + "Weld.Material_Grade_OverWrite", + "Detailing.Gap" + ] + for key in numeric_fields: + if key in filtered_input: + value = filtered_input[key] + if not isinstance(value, str) or not float_able(value): + raise InvalidInputTypeError( + f"Field '{key}' must be a string that can be converted to float, got '{value}'" + ) + # Additional numeric validation + float_val = float(value) + if key == "Detailing.Gap" and float_val < 0: + raise ValueError(f"Gap value must be non-negative, got {float_val}") + + # Validate plate thickness lists + thickness_lists = [ + "Connector.Flange_Plate.Thickness_list", + "Connector.Web_Plate.Thickness_List" + ] + for key in thickness_lists: + if key in filtered_input: + thickness = filtered_input[key] + if not isinstance(thickness, list): + raise InvalidInputTypeError( + f"Field '{key}' must be a list, got {type(thickness).__name__}" + ) + if not validate_list_type(thickness, str): + raise InvalidInputTypeError( + f"All items in '{key}' must be strings" + ) + if not custom_list_validation(thickness, float_able): + invalid_items = [x for x in thickness if not float_able(x)] + raise InvalidInputTypeError( + f"All items in '{key}' must be convertible to float. Invalid items: {invalid_items}" + ) + + except Exception as e: + raise type(e)(f"Input validation failed: {str(e)}") + + +def create_module() -> BeamCoverPlateWeld: + """Create an instance of the cover plate welded connection module design class and set it up for use""" + module = BeamCoverPlateWeld() + print('MODULE', module) # Create module instance. + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> BeamCoverPlateWeld: + """Create an instance of the cover plate welded connection module design class from input values.""" + module = create_module() + print('INPUT SET FOR FINAL OUTPUT', input_values) # Create module instance. + module.set_input_values(input_values) + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate, format and return formatted output""" + output = {} + module = create_from_input(input_values) + + # Get raw output data + raw_output_text = module.output_values(True) + raw_member_capacity = module.member_capacityoutput(True) + flange_weld_details = module.flange_weld_details(True) + web_weld_details = module.web_weld_details(True) + web_capacity = module.webcapacity(True) + flange_capacity = module.flangecapacity(True) + web_block_shear_pattern = module.web_pattern(True) + + from osdag_core.custom_logger import CustomLogger + logs = [] + if hasattr(module, "logger") and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() or [] + else: + logs = getattr(module, "logs", []) or [] + raw_output = ( + raw_output_text + + raw_member_capacity + + flange_weld_details + + web_weld_details + + web_capacity + + flange_capacity + + web_block_shear_pattern + ) + print('RAW OUTPUT', raw_output) # Debugging output + # Format output + for param in raw_output: + if param[2] == "TextBox": + key = param[0] + label = param[1] + value = param[3] + output[key] = { + "key": key, + "label": label, + "val": value # Changed from "value" to "val" to match frontend expectations + } + return output, logs + + +# def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: +# """Generate the CAD model from input values as a BREP file. Return file path.""" +# if section not in ("Model", "Beam", "Connector"): # Error checking: If section is valid. +# raise InvalidInputTypeError( +# "section", "'Model', 'Beam' or 'Connector'") +# module = create_from_input(input_values) # Create module from input. +# # Object that will create the CAD model. +# try : +# cld = CommonDesignLogic(None, '', module.module , module.mainmodule) +# print('CAD MODULE', module.mainmodule) # Create CommonDesignLogic instance. +# except Exception as e : +# print('error in cld e : ' , e) + +# try : +# # Setup the calculations object for generating CAD model. +# setup_for_cad(cld, module) +# except Exception as e : +# traceback.print_exc() +# print('Error in setting up cad e : ' , e) + +# # The section of the module that will be generated. +# cld.component = section + +# try : +# model = cld.create2Dcad() # Generate CAD Model. +# except Exception as e : +# print('Error in cld.create2Dcad() e : ' , e) +# return False + +# # check if the cad_models folder exists or not +# # if no, then create one +# if(not os.path.exists(os.path.join(os.getcwd() , "file_storage/cad_models/"))) : +# print('path does not exists cad_models , creating one') +# os.mkdir(os.path.join(os.getcwd() , "file_storage/cad_models/")) + +# print('2d model : ' , model) +# # os.system("clear") # clear the terminal +# file_name = session + "_" + section + ".brep" +# file_path = "file_storage/cad_models/" + file_name +# print('brep file path in create_cad_model : ' , file_path) + +# try : +# BRepTools.breptools.Write(model, file_path) # Generate CAD Model +# except Exception as e : +# print('Writing to BREP file failed e : ' , e) + +# return file_path + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP/STL file. + + External API uses section names: "Model", "Beam", "CoverPlate". + Internally, the legacy CAD logic for beam cover plate welded uses component + name "Connector" for the cover plate + bolts assembly. Map CoverPlate -> + Connector for CAD routing, but keep the external section name for file + naming and response keys. + """ + if section not in ("Model", "Beam", "CoverPlate"): + raise InvalidInputTypeError("section", "'Model', 'Beam' or 'CoverPlate'") + + module = create_from_input(input_values) + + from osdag_core.Common import KEY_DISP_BEAMCOVERPLATEWELD + if getattr(module, "module", None) != KEY_DISP_BEAMCOVERPLATEWELD: + module.module = KEY_DISP_BEAMCOVERPLATEWELD + module.mainmodule = "Moment Connection" + + print(f"[CAD DEBUG] BB cover plate welded: module={module.module}, mainmodule={module.mainmodule}, section={section}") + # CommonDesignLogic(display, cad_widget, folder, connection, mainmodule) + cld = CommonDesignLogic(None, "", "", module.module, module.mainmodule) + setup_for_cad(cld, module) + + # Map external section names to internal component names expected by CommonDesignLogic + internal_section = section + if section == "CoverPlate": + internal_section = "Connector" + + cld.component = internal_section + print(f"[cadissue] BB cover plate welded: cld.component set to {internal_section} for section={section}") + model = cld.create2Dcad() + + # check if the cad_models folder exists or not + # if no, then create one + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + + print('2d model : ' , model) + # os.system("clear") # clear the terminal + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + print('brep file path in create_cad_model : ' , file_path) + + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) # Generate CAD Model + + # Always try to write STL for the requested section + try: + stl_rel = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_rel) + write_stl(model, full_stl) + print(f"STL file saved at {full_stl}") + except Exception as stle: + print(f"Warning: Failed to save STL at {file_path}: {stle}") + + if section == "Model": + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + + except Exception as e: + print('Writing to BREP/STL file failed e : ', e) + + return file_path \ No newline at end of file diff --git a/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_welded/service.py b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_welded/service.py new file mode 100644 index 000000000..9074fabc8 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_beam_cover_plate_welded/service.py @@ -0,0 +1,31 @@ +""" +Cover Plate Welded Service - Business logic layer +Bridges between API and osdag_core +""" +from osdag_core.design_type.connection.beam_cover_plate_weld import BeamCoverPlateWeld +from .adapter import validate_input, generate_output, create_cad_model + + +class CoverPlateWeldedService: + """Service class for Cover Plate Welded Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + validate_input(inputs) + model = BeamCoverPlateWeld() + if hasattr(model, "set_osdaglogger"): + model.set_osdaglogger(None, id="web") + model.set_input_values(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs, + 'success': True + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/moment_connection/submodules/beam_beam_end_plate/__init__.py b/backend/apps/modules/moment_connection/submodules/beam_beam_end_plate/__init__.py new file mode 100644 index 000000000..92ac03780 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_beam_end_plate/__init__.py @@ -0,0 +1,6 @@ +""" +Beam Beam End Plate Connection Sub-module +""" +MODULE_ID = 'Beam-Beam-End-Plate-Connection' +from .service import BeamBeamEndPlateService as Service + diff --git a/backend/apps/modules/moment_connection/submodules/beam_beam_end_plate/adapter.py b/backend/apps/modules/moment_connection/submodules/beam_beam_end_plate/adapter.py new file mode 100644 index 000000000..58509ce9b --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_beam_end_plate/adapter.py @@ -0,0 +1,429 @@ +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl, +) +from ...shared import setup_for_cad # Use moment_connection shared utilities +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from osdag_core.cad.common_logic import CommonDesignLogic +# Will log a lot of unnessecary data. +from osdag_core.design_type.connection.beam_beam_end_plate_splice import BeamBeamEndPlateSplice +import sys +import os +from typing import Dict, Any, List +import traceback + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + return [ + "Bolt.Bolt_Hole_Type", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Slip_Factor", + "Bolt.TensionType", + "Bolt.Type", + "Connectivity *", + "EndPlateType", + "Connector.Plate.Thickness_List", + "Connector.Material", + "Design.Design_Method", + "Detailing.Corrosive_Influences", + "Detailing.Edge_type", + "Detailing.Gap", + "Load.Axial", + "Load.Moment", + "Load.Shear", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Module", + "Weld.Fab", + "Weld.Material_Grade_OverWrite", + "Weld.Type" + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + raise MissingKeyError(missing_keys[0]) + + # Validate key types one by one: + + # Validate Bolt.Bolt_Hole_Type. + # Check if Bolt.Bolt_Hole_Type is a string. + if not isinstance(input_values["Bolt.Bolt_Hole_Type"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type", "str") + + # Validate Bolt.Diameter. + bolt_diameter = input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter, list) # Check if Bolt.Diameter is a list. + # Check if all items in Bolt.Diameter are str. + or not validate_list_type(bolt_diameter, str) + or not custom_list_validation(bolt_diameter, int_able)): # Check if all items in Bolt.Diameter can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Diameter", "non empty List[str] where all items can be converted to int") + + # Validate Bolt.Grade + bolt_grade = input_values["Bolt.Grade"] + if (not isinstance(bolt_grade, list) # Check if Bolt.Grade is a list. + # Check if all items in Bolt.Grade are str. + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): # Check if all items in Bolt.Grade can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Grade", "non empty List[str] where all items can be converted to float") + + # Validate Bolt.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor, str) # Check if Bolt.Slip_Factor is a string. + or not float_able(bolt_slipfactor)): # Check if Bolt.Slip_Factor can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Slip_Factor", "str where str can be converted to float") + + # Validate Bolt.TensionType + # Check if Bolt.TensionType is a string. + if not isinstance(input_values["Bolt.TensionType"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.TensionType", "str") + + # Validate Bolt.Type + # Check if Bolt.Type is a string. + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") # If not, raise error. + + # Validate Connectivity + # Check if Connectivity is a string. + if not isinstance(input_values["Connectivity *"], str): + # If not, raise error. + raise InvalidInputTypeError("Connectivity *", "str") + + # Validate Connectivity + # Check if Connectivity is a string. + if not isinstance(input_values["EndPlateType"], str): + # If not, raise error. + raise InvalidInputTypeError("EndPlateType", "str") + + # Validate Connector.Flange_Plate.Thickness_list + thickness_list = input_values["Connector.Plate.Thickness_List"] + if (not isinstance(thickness_list, list) + or not validate_list_type(thickness_list, str) + or not custom_list_validation(thickness_list, int_able)): + raise InvalidInputTypeError( + "Connector.Plate.Thickness_List", "List[str] where all items can be converted to int") + # Validate Connector.Material + # Check if Connector.Material is a string. + if not isinstance(input_values["Connector.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + # Check if Design.Design_Method is a string. + if not isinstance(input_values["Design.Design_Method"], str): + # If not, raise error. + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + # Check if Detailing.Corrosive_Influences is 'Yes' or 'No'. + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + # If not, raise error. + raise InvalidInputTypeError( + "Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + # Check if Detailing.Edge_type is a string. + if not isinstance(input_values["Detailing.Edge_type"], str): + # If not, raise error. + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) # Check if Detailing.Gap is a string. + or not int_able(detailing_gap)): # Check if Detailing.Gap can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) # Check if Load.Axial is a string. + or not int_able(load_axial)): # Check if Load.Axial can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Axial", "str where str can be converted to int") + + # Validate Load.Moment + load_moment = input_values["Load.Moment"] + if not isinstance(load_moment, str) or not int_able(load_moment): + raise InvalidInputTypeError("Load.Moment", "str where str can be converted to int") + + # Validate Load.Shear + load_shear = input_values["Load.Shear"] + if (not isinstance(load_shear, str) # Check if Load.Shear is a string. + or not int_able(load_shear)): # Check if Load.Shear can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Shear", "str where str can be converted to int") + + # Validate Material + # Check if Material is a string. + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") # If not, raise error. + + # Validate Member.Designation + if not isinstance(input_values["Member.Supported_Section.Designation"], str): + raise InvalidInputTypeError("Member.Supported_Section.Designation", "str") + + # Validate Member.Material + if not isinstance(input_values["Member.Supported_Section.Material"], str): + raise InvalidInputTypeError("Member.Supported_Section.Material", "str") + + # Check if Module is a string. + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") # If not, raise error. + + # Validate Weld.Fab + # Check if Weld.Fab is a string. + if not isinstance(input_values["Weld.Fab"], str): + raise InvalidInputTypeError("Weld.Fab", "str") # If not, raise error. + + # Validate Weld.Material_Grade_OverWrite + weld_materialgradeoverwrite = input_values["Weld.Material_Grade_OverWrite"] + if (not isinstance(weld_materialgradeoverwrite, str) # Check if Weld.Material_Grade_OverwWite is a string. + or not int_able(weld_materialgradeoverwrite)): # Check if Weld.Material_Grade_OverWrite can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Weld.Material_Grade_OverWrite", "str where str can be converted to int.") + + # Validate Weld.Type + # Check if Weld.Type is a string. + if not isinstance(input_values["Weld.Type"], str): + raise InvalidInputTypeError("Weld.Type", "str") # If not, raise error. + +def validate_input_new(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + print('required_keys : ' , required_keys) + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + print('missing keys : ' , missing_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + print("missing keys is not None") + raise MissingKeyError(missing_keys[0]) + + # Validate key types using loops. + + # Validate all strings. + str_keys = ["Bolt.Bolt_Hole_Type", # List of all parameters that are strings + "Bolt.TensionType", + "Bolt.Type", + "Connectivity *", + "EndPlateType", + "Bolt.Connector_Material", + "Connector.Flange_Plate.Preferences", + "Design.Design_Method", + "Detailing.Edge_type", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Module", + "Weld.Fab", + "Weld.Type" + ] + for key in str_keys: # Loop through all keys. + print('validating string key') + + try : + validate_string(key) # Check if key is a string. If not, raise error. + except : + print('error in validating string keys') + print('string key passed : ' , key ) + + # Validate for keys that are numbers + num_keys = [("Bolt.Slip_Factor", True), # List of all parameters that are numbers (key, is_float) + ("Detailing.Gap", False), + ("Load.Axial", False), + ("Load.Moment", False), + ("Load.Shear", False), + ("Weld.Material_Grade_OverWrite", False) + ] + for key in num_keys: # Loop through all keys. + # Check if key is a number. If not, raise error. + print('validating num keys') + validate_num(key[0], key[1]) + + # Validate for keys that are arrays + arr_keys = [("Bolt.Diameter", False), # List of all parameters that can be converted to numbers (key, is_float) + ("Bolt.Grade", True), + ("Connector.Plate.Thickness_List", False)] + for key in arr_keys: + print('validating arr key') + # Check if key is a list where all items can be converted to numbers. If not, raise error. + validate_arr(key[0], key[1]) + + +def create_module() -> BeamBeamEndPlateSplice: + """Create an instance of the beambeam end plate connection module design class and set it up for use""" + module = BeamBeamEndPlateSplice() # Create an instance of the BeamBeamEndPlateConnection + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> BeamBeamEndPlateSplice: + """Create an instance of the beam beam end plate connection module design class from input values.""" + # validate_input(input_values) + try : + module = create_module() # Create module instance. + except Exception as e : + print('e in create_module : ' , e) + print('error in creating module') + + # Set the input values on the module instance. + try : + print("**********", input_values) + module.set_input_values(input_values) + except Exception as e : + traceback.print_exc() + print('e in set_input_values ************ : ' , e) + print('error in setting the input values **********') + + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """ + Generate, format and return the input values from the given output values. + Output format (json): { + "Bolt.Pitch": + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)" + "value": 40 + } + } + """ + print("************") + output = {} # Dictionary for formatted values + module = create_from_input(input_values) # Create module from input. + print('module : ******' , module) + print('type of module : ******** ' , type(module)) + # Generate output values in unformatted form. + raw_output_text = module.output_values(True) + stiffener_output = module.stiffener_details(True) + + from osdag_core.custom_logger import CustomLogger + if hasattr(module, "logger") and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() or [] + else: + logs = getattr(module, "logs", []) or [] + raw_output = raw_output_text + stiffener_output + + # os.system("clear") + # Loop over all the text values and add them to ouptut dict. + for param in raw_output: + if param[2] == "TextBox": # If the parameter is a text output, + key = param[0] # id/key + label = param[1] # label text. + value = param[3] # Value as string. + output[key] = { + "key": key, + "label": label, + "val": value # Changed from "value" to "val" to match frontend expectations + } # Set label, key and value in output + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + if section not in ("Model", "Beam", "EndPlate"): + raise InvalidInputTypeError("section", "'Model', 'Beam' or 'EndPlate'") + + module = create_from_input(input_values) + from osdag_core.Common import KEY_DISP_BB_EP_SPLICE + # Force correct keys for CAD routing + if getattr(module, "module", None) != KEY_DISP_BB_EP_SPLICE: + print(f"[CAD DEBUG] Adjusting module.module from {getattr(module,'module',None)} to {KEY_DISP_BB_EP_SPLICE}") + module.module = KEY_DISP_BB_EP_SPLICE + if getattr(module, "mainmodule", None) != "Moment Connection": + print(f"[CAD DEBUG] Adjusting module.mainmodule from {getattr(module,'mainmodule',None)} to Moment Connection") + module.mainmodule = "Moment Connection" + + print(f"[CAD DEBUG] building CommonDesignLogic with module={module.module}, mainmodule={module.mainmodule}, section={section}") + # CommonDesignLogic(display, cad_widget, folder, connection, mainmodule) + cld = CommonDesignLogic(None, "", "", module.module, module.mainmodule) + setup_for_cad(cld, module) + + cld.component = section + model = cld.create2Dcad() + + # check if the cad_models folder exists or not + # if no, then create one + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + + print('2d model : ' , model) + # os.system("clear") # clear the terminal + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + print('brep file path in create_cad_model : ' , file_path) + + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) # Generate CAD Model + + # Always try to write STL for the requested section + try: + stl_rel = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_rel) + write_stl(model, full_stl) + print(f"STL file saved at {full_stl}") + except Exception as stle: + print(f"Warning: Failed to save STL at {file_path}: {stle}") + + if section == "Model": + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + + except Exception as e: + print('Writing to BREP/STL file failed e : ', e) + + return file_path + + diff --git a/backend/apps/modules/moment_connection/submodules/beam_beam_end_plate/service.py b/backend/apps/modules/moment_connection/submodules/beam_beam_end_plate/service.py new file mode 100644 index 000000000..24d040751 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_beam_end_plate/service.py @@ -0,0 +1,32 @@ +""" +Beam Beam End Plate Service - Business logic layer +Bridges between API and osdag_core +""" +from osdag_core.design_type.connection.beam_beam_end_plate_splice import BeamBeamEndPlateSplice +from .adapter import validate_input, generate_output, create_cad_model + + +class BeamBeamEndPlateService: + """Service class for Beam Beam End Plate Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + validate_input(inputs) + model = BeamBeamEndPlateSplice() + # Initialize logger to avoid NoneType errors on .info usage + if hasattr(model, "set_osdaglogger"): + model.set_osdaglogger(None, id="web") + model.set_input_values(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs, + 'success': True + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/moment_connection/submodules/beam_column_end_plate/__init__.py b/backend/apps/modules/moment_connection/submodules/beam_column_end_plate/__init__.py new file mode 100644 index 000000000..b8010fd1a --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_column_end_plate/__init__.py @@ -0,0 +1,6 @@ +""" +Beam Column End Plate Connection Sub-module +""" +MODULE_ID = 'Beam-to-Column-End-Plate-Connection' +from .service import BeamColumnEndPlateService as Service + diff --git a/backend/apps/modules/moment_connection/submodules/beam_column_end_plate/adapter.py b/backend/apps/modules/moment_connection/submodules/beam_column_end_plate/adapter.py new file mode 100644 index 000000000..43aa97b5d --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_column_end_plate/adapter.py @@ -0,0 +1,674 @@ +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl +) +from ...shared import setup_for_cad # Use moment_connection shared utilities +from osdag_core.cad.common_logic import CommonDesignLogic +from OCC.Core import BRepTools +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from osdag_core.custom_logger import CustomLogger +# Will log a lot of unnessecary data. +from osdag_core.design_type.connection.beam_column_end_plate import BeamColumnEndPlate +import sys +import os +import typing +import traceback +import logging +import json +from typing import Dict, Any, List + +# Configure logger +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +# Create handlers +c_handler = logging.StreamHandler() +f_handler = logging.FileHandler('beam_column_endplate_module.log') +c_handler.setLevel(logging.INFO) +f_handler.setLevel(logging.DEBUG) +# Create formatters and add it to handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +c_handler.setFormatter(formatter) +f_handler.setFormatter(formatter) +# Add handlers to the logger +logger.addHandler(c_handler) +logger.addHandler(f_handler) + +# Suppress output when necessary +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + return [ + "Bolt.Bolt_Hole_Type", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Slip_Factor", + "Bolt.TensionType", + "Bolt.Type", + "Connectivity", + "EndPlateType", + "Connector.Material", + "Design.Design_Method", + "Detailing.Corrosive_Influences", + "Detailing.Edge_type", + "Detailing.Gap", + "Load.Axial", + "Load.Shear", + "Load.Moment", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab", + "Weld.Material_Grade_OverWrite", + "Weld.Type", + "Connector.Plate.Thickness_List", + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + raise MissingKeyError(missing_keys[0]) + + # Validate key types one by one: + + # Validate Bolt.Bolt_Hole_Type. + # Check if Bolt.Bolt_Hole_Type is a string. + if not isinstance(input_values["Bolt.Bolt_Hole_Type"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type", "str") + + # Validate Bolt.Diameter. + bolt_diameter = input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter, list) # Check if Bolt.Diameter is a list. + # Check if all items in Bolt.Diameter are str. + or not validate_list_type(bolt_diameter, str) + or not custom_list_validation(bolt_diameter, int_able)): # Check if all items in Bolt.Diameter can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Diameter", "non empty List[str] where all items can be converted to int") + + # Validate Bolt.Grade + bolt_grade = input_values["Bolt.Grade"] + if (not isinstance(bolt_grade, list) # Check if Bolt.Grade is a list. + # Check if all items in Bolt.Grade are str. + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): # Check if all items in Bolt.Grade can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Grade", "non empty List[str] where all items can be converted to float") + + # Validate Bolt.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor, str) # Check if Bolt.Slip_Factor is a string. + or not float_able(bolt_slipfactor)): # Check if Bolt.Slip_Factor can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Slip_Factor", "str where str can be converted to float") + + # Validate Bolt.TensionType + # Check if Bolt.TensionType is a string. + if not isinstance(input_values["Bolt.TensionType"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.TensionType", "str") + + # Validate Bolt.Type + # Check if Bolt.Type is a string. + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") # If not, raise error. + + # Validate Connectivity + # Check if Connectivity is a string. + if not isinstance(input_values["Connectivity"], str): + # If not, raise error. + raise InvalidInputTypeError("Connectivity", "str") + + # Validate EndPlateType + # Check if EndPlateType is a string. + if not isinstance(input_values["EndPlateType"], str): + # If not, raise error. + raise InvalidInputTypeError("EndPlateType", "str") + + # Validate Connector.Material + # Check if Connector.Material is a string. + if not isinstance(input_values["Connector.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + # Check if Design.Design_Method is a string. + if not isinstance(input_values["Design.Design_Method"], str): + # If not, raise error. + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + # Check if Detailing.Corrosive_Influences is 'Yes' or 'No'. + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + # If not, raise error. + raise InvalidInputTypeError( + "Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + # Check if Detailing.Edge_type is a string. + if not isinstance(input_values["Detailing.Edge_type"], str): + # If not, raise error. + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) # Check if Detailing.Gap is a string. + or not int_able(detailing_gap)): # Check if Detailing.Gap can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) # Check if Load.Axial is a string. + or not int_able(load_axial)): # Check if Load.Axial can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Axial", "str where str can be converted to int") + + # Validate Load.Shear + load_shear = input_values["Load.Shear"] + if (not isinstance(load_shear, str) # Check if Load.Shear is a string. + or not int_able(load_shear)): # Check if Load.Shear can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Shear", "str where str can be converted to int") + + # Validate Load.Moment + load_moment = input_values["Load.Moment"] + if (not isinstance(load_moment, str) # Check if Load.Moment is a string. + or not int_able(load_moment)): # Check if Load.Moment can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Moment", "str where str can be converted to int") + + # Validate Material + # Check if Material is a string. + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") # If not, raise error. + + # Validate Member.Supported_Section.Designation + # Check if Member.Supported_Section.Designation is a string. + if not isinstance(input_values["Member.Supported_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supported_Section.Designation", "str") + + # Validate Member.Supported_Section.Material + # Check if Member.Supported_Section.Material is a string. + if not isinstance(input_values["Member.Supported_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Member.Supported_Section.Material", "str") + + # Validate Member.Supporting_Section.Designation + # Check if Member.Supporting_Section.Designation is a string. + if not isinstance(input_values["Member.Supporting_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Designation", "str") + + # Validate Member.Supporting_Section.Material + # Check if Member.Supporting_Section.Material is a string. + if not isinstance(input_values["Member.Supporting_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Material", "str") + + # Validate Module + # Check if Module is a string. + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") # If not, raise error. + + # Validate Weld.Fab + # Check if Weld.Fab is a string. + if not isinstance(input_values["Weld.Fab"], str): + raise InvalidInputTypeError("Weld.Fab", "str") # If not, raise error. + + # Validate Weld.Type + # Check if Weld.Type is a string. + if not isinstance(input_values["Weld.Type"], str): + raise InvalidInputTypeError("Weld.Type", "str") # If not, raise error. + + # Validate Weld.Material_Grade_OverWrite + weld_materialgradeoverwrite = input_values["Weld.Material_Grade_OverWrite"] + if (not isinstance(weld_materialgradeoverwrite, str) # Check if Weld.Material_Grade_OverwWite is a string. + or not int_able(weld_materialgradeoverwrite)): # Check if Weld.Material_Grade_OverWrite can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Weld.Material_Grade_OverWrite", "str where str can be converted to int.") + + # Validate Connector.Plate.Thickness_List + connector_plate_thicknesslist = input_values["Connector.Plate.Thickness_List"] + if (not isinstance(connector_plate_thicknesslist, list) # Check if Connector.Plate.Thickness_List is a list. + # Check if all items in Connector.Plate.Thickness_List are str. + or not validate_list_type(connector_plate_thicknesslist, str) + or not custom_list_validation(connector_plate_thicknesslist, int_able)): # Check if all items in Connector.Plate.Thickness_List can be converted to int. + raise InvalidInputTypeError( + "Connector.Plate.Thickness_List", "List[str] where all items can be converted to int") + + +def validate_input_new(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + logger.info("Validating input values using new method") + + # Check if all required keys exist + required_keys = get_required_keys() + logger.debug(f"Required keys: {required_keys}") + + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + logger.debug(f"Missing keys: {missing_keys}") + + if missing_keys is not None: # If keys are missing. + # Raise error for the first missing key. + logger.error(f"Missing key detected: {missing_keys[0]}") + raise MissingKeyError(missing_keys[0]) + + # Validate key types using loops. + try: + # Validate all strings. + str_keys = ["Bolt.Bolt_Hole_Type", # List of all parameters that are strings + "Bolt.TensionType", + "Bolt.Type", + "Connectivity", + "EndPlateType", + "Connector.Material", + "Design.Design_Method", + "Detailing.Edge_type", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab", + "Weld.Type"] + + logger.debug(f"Validating string keys: {str_keys}") + for key in str_keys: # Loop through all keys. + logger.debug(f"Validating string key: {key}") + try: + # Check if the value for the key is a string. If not, raise error. + if key in input_values: + if not isinstance(input_values[key], str): + logger.error(f"Key {key} should be a string but got {type(input_values[key])}") + raise InvalidInputTypeError(key, "str") + logger.debug(f"Key {key} validated successfully") + else: + logger.warning(f"Key {key} not found in input_values") + except Exception as e: + logger.error(f"Error validating string key {key}: {str(e)}") + logger.error(traceback.format_exc()) + raise + + # Validate for keys that are numbers + num_keys = [("Bolt.Slip_Factor", True), # List of all parameters that are numbers (key, is_float) + ("Detailing.Gap", False), + ("Load.Axial", False), + ("Load.Shear", False), + ("Load.Moment", False), + ("Weld.Material_Grade_OverWrite", False)] + + logger.debug(f"Validating number keys: {num_keys}") + for key_info in num_keys: # Loop through all keys. + key, is_float = key_info + logger.debug(f"Validating number key: {key}, is_float: {is_float}") + try: + # Check if key is a string that can be converted to a number. If not, raise error. + if key in input_values: + value = input_values[key] + if not isinstance(value, str): + logger.error(f"Key {key} should be a string but got {type(value)}") + raise InvalidInputTypeError(key, "str where str can be converted to " + ("float" if is_float else "int")) + if is_float: + if not float_able(value): + logger.error(f"Key {key} should be convertible to float but got '{value}'") + raise InvalidInputTypeError(key, "str where str can be converted to float") + else: + if not int_able(value): + logger.error(f"Key {key} should be convertible to int but got '{value}'") + raise InvalidInputTypeError(key, "str where str can be converted to int") + logger.debug(f"Key {key} validated successfully") + else: + logger.warning(f"Key {key} not found in input_values") + except Exception as e: + logger.error(f"Error validating number key {key}: {str(e)}") + logger.error(traceback.format_exc()) + raise + + # Validate for keys that are arrays + arr_keys = [("Bolt.Diameter", False), # List of all parameters that can be converted to numbers (key, is_float) + ("Bolt.Grade", True), + ("Connector.Plate.Thickness_List", False)] + + logger.debug(f"Validating array keys: {arr_keys}") + for key_info in arr_keys: + key, is_float = key_info + logger.debug(f"Validating array key: {key}, is_float: {is_float}") + try: + # Check if key is a list where all items can be converted to numbers. If not, raise error. + if key in input_values: + value = input_values[key] + if not isinstance(value, list): + logger.error(f"Key {key} should be a list but got {type(value)}") + raise InvalidInputTypeError(key, "list") + if not validate_list_type(value, str): + logger.error(f"Key {key} should be a list of strings") + raise InvalidInputTypeError(key, "list of strings") + if is_float: + if not custom_list_validation(value, float_able): + logger.error(f"Key {key} should be a list of strings convertible to float") + raise InvalidInputTypeError(key, "list of strings convertible to float") + else: + if not custom_list_validation(value, int_able): + logger.error(f"Key {key} should be a list of strings convertible to int") + raise InvalidInputTypeError(key, "list of strings convertible to int") + logger.debug(f"Key {key} validated successfully") + else: + logger.warning(f"Key {key} not found in input_values") + except Exception as e: + logger.error(f"Error validating array key {key}: {str(e)}") + logger.error(traceback.format_exc()) + raise + + logger.info("Input validation completed successfully") + + except Exception as e: + logger.error(f"Validation error: {str(e)}") + logger.error(traceback.format_exc()) + raise + + +def create_module() -> BeamColumnEndPlate: + """Create an instance of the Beam Column End plate connection module design class and set it up for use""" + logger.info("Creating BeamColumnEndPlate module instance") + try: + module = BeamColumnEndPlate() # Create an instance of the BeamColumnEndPlate + logger.debug("BeamColumnEndPlate instance created successfully") + module.set_osdaglogger(None, id="web") + logger.debug("OSDAGLogger set to None") + return module + except Exception as e: + logger.error(f"Error creating BeamColumnEndPlate module: {str(e)}") + logger.error(traceback.format_exc()) + raise + + +def create_from_input(input_values: Dict[str, Any]) -> BeamColumnEndPlate: + """Create an instance of the Beam Column End plate connection module design class from input values.""" + logger.info("Creating module from input values") + + # Validate input values first to catch issues early + try: + logger.debug("Validating input values") + validate_input(input_values) + logger.debug("Input validation successful") + except Exception as e: + logger.error(f"Input validation failed: {str(e)}") + logger.error(traceback.format_exc()) + raise + + try: + logger.debug("Creating module instance") + module = create_module() # Create module instance. + logger.debug("Module instance created successfully") + except Exception as e: + logger.error(f"Error in create_module: {str(e)}") + logger.error(traceback.format_exc()) + raise + + # Set the input values on the module instance. + try: + logger.debug("Setting input values on module instance") + logger.debug(f"Input values: {input_values}") + module.set_input_values(input_values) + logger.debug("Input values set successfully") + except Exception as e: + logger.error(f"Error in set_input_values: {str(e)}") + logger.error(traceback.format_exc()) + raise + + logger.info("Module created and initialized with input values successfully") + return module + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate, format and return formatted output""" + logger.info("Generating output for Beam-to-Column End Plate") + output = {} + logs = [] # Initialize logs + + try: + module = create_from_input(input_values) + + # Get raw output data + raw_output_text = module.output_values(True) + detailing = module.detailing(True) + continuity_plate_details = module.continuity_plate_details(True) + stiffener_plate_details = module.web_stiffener_plate_details(True) + stiffener_details = module.stiffener_details(True) + stiffener_detailing = module.stiffener_detailing(True) + weld_details = module.weld_details(True) + + from osdag_core.custom_logger import CustomLogger + if hasattr(module, "logger") and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() or [] + print(f'Retrieved {len(logs)} logs from custom logger') + else: + logs = getattr(module, "logs", []) or [] + + raw_output = ( + raw_output_text + + detailing + continuity_plate_details + + stiffener_plate_details + stiffener_details + + stiffener_detailing + weld_details + ) + + # Format output + for param in raw_output: + if len(param) >= 4 and param[2] == "TextBox": + key = param[0] + label = param[1] + value = param[3] + + # Handle numpy types + if hasattr(value, 'item'): + value = value.item() + + output[key] = { + "key": key, + "label": label, + "val": value + } + + logger.info(f"Output generation completed. Generated {len(output)} output fields and {len(logs)} log messages") + + except Exception as e: + logger.error(f"Error in generate_output: {str(e)}") + logger.error(traceback.format_exc()) + # Return what we have so far + logger.debug(f"Final logs being returned: {logs}") + return output, logs + + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + if section == "Plate": + section = "Connector" + if section not in ("Model", "Beam", "Column", "Connector"): + raise InvalidInputTypeError("section", "'Model', 'Beam', 'Column' or 'Connector'") + + module = create_from_input(input_values) + from osdag_core.Common import KEY_DISP_BCENDPLATE + if getattr(module, "module", None) != KEY_DISP_BCENDPLATE: + print(f"[CAD DEBUG] Adjusting module.module from {getattr(module,'module',None)} to {KEY_DISP_BCENDPLATE}") + module.module = KEY_DISP_BCENDPLATE + if getattr(module, "mainmodule", None) != "Moment Connection": + print(f"[CAD DEBUG] Adjusting module.mainmodule from {getattr(module,'mainmodule',None)} to Moment Connection") + module.mainmodule = "Moment Connection" + + print(f"[CAD DEBUG] building CommonDesignLogic with module={module.module}, mainmodule={module.mainmodule}, section={section}") + # CommonDesignLogic(display, cad_widget, folder, connection, mainmodule) + cld = CommonDesignLogic(None, "", "", module.module, module.mainmodule) + setup_for_cad(cld, module) + + # The section of the module that will be generated. + cld.component = section + + # When section == "Model", also ensure per-part shapes exist and prepare a compound + # Try to include additional subparts like Welds and Bolts if available + part_names = ["Beam", "Column", "Connector"] + part_files = {} + compound_model = None + + try: + if section == "Model": + # Build compound by adding each part shape without fusing + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + + for part in part_names: + try: + # Generate shape for this part + cld.component = part + part_shape = cld.create2Dcad() + if part_shape is None: + continue + + # Add to compound + builder.Add(compound, part_shape) + + # Ensure per-part BREP file exists (write or overwrite) + part_file_name = f"{session}_{part}.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + BRepTools.breptools.Write(part_shape, part_file_path_rel, Message_ProgressRange()) + part_files[part] = part_file_path_rel + # Also write STL for this part + try: + part_stl_rel = part_file_path_rel.replace(".brep", ".stl") + write_stl(part_shape, os.path.join(os.getcwd(), part_stl_rel)) + except Exception as stle: + print(f"Failed to write STL for part {part}: {stle}") + except Exception as e: + print(f"Failed to build/write part {part}: {e}") + + # Reset component to Model and set compound as the model to write + cld.component = section + compound_model = compound + # Generate model for non-Model sections (or fallback) + if compound_model is not None: + model = compound_model + else: + model = cld.create2Dcad() + except Exception as e : + print('Error in cld.create2Dcad() e : ' , e) + return False + + # check if the cad_models folder exists or not + # if no, then create one + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + + print('2d model : ' , model) + # os.system("clear") # clear the terminal + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + print('brep file path in create_cad_model : ' , file_path) + + try : + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) # Generate CAD Model + + # Always try to write STL for the requested section + try: + stl_rel = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_rel) + write_stl(model, full_stl) + print(f"STL file saved at {full_stl}") + except Exception as stle: + print(f"Warning: Failed to save STL at {file_path}: {stle}") + + # If it's 'Model' section, write a manifest referencing per-part breps and save extra formats + if section == "Model": + try: + manifest = { + "session": session, + "mergedBrep": file_path, + "parts": [ + {"name": name, "brepPath": part_files.get(name)} for name in part_names if part_files.get(name) + ] + } + # add stlPath for parts + for entry in manifest["parts"]: + if entry.get("brepPath"): + entry["stlPath"] = entry["brepPath"].replace(".brep", ".stl") + manifest_path = file_path.replace(".brep", ".parts.json") + full_manifest_path = os.path.join(os.getcwd(), manifest_path) + with open(full_manifest_path, "w", encoding="utf-8") as mf: + json.dump(manifest, mf) + print(f"Parts manifest saved at {full_manifest_path}") + except Exception as me: + print(f"Warning: Failed to write manifest: {me}") + + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + # Write merged STL for Model + try: + merged_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), merged_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), merged_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save merged STL: {stle}") + except Exception as e : + print('Writing to BREP file failed e : ' , e) + + # For non-Model sections, export single STL next to BREP + if section != "Model": + try: + single_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), single_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save STL for {section}: {stle}") + + return file_path \ No newline at end of file diff --git a/backend/apps/modules/moment_connection/submodules/beam_column_end_plate/service.py b/backend/apps/modules/moment_connection/submodules/beam_column_end_plate/service.py new file mode 100644 index 000000000..705e61058 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/beam_column_end_plate/service.py @@ -0,0 +1,31 @@ +""" +Beam Column End Plate Service - Business logic layer +Bridges between API and osdag_core +""" +from osdag_core.design_type.connection.beam_column_end_plate import BeamColumnEndPlate +from .adapter import validate_input, generate_output, create_cad_model + + +class BeamColumnEndPlateService: + """Service class for Beam Column End Plate Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + validate_input(inputs) + model = BeamColumnEndPlate() + if hasattr(model, "set_osdaglogger"): + model.set_osdaglogger(None, id="web") + model.set_input_values(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs, + 'success': True + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_bolted/__init__.py b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_bolted/__init__.py new file mode 100644 index 000000000..345de04bd --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_bolted/__init__.py @@ -0,0 +1,5 @@ +""" +Column Cover Plate Bolted Connection Sub-module +""" +MODULE_ID = 'ColumnCoverPlateBolted' +from .service import ColumnCoverPlateBoltedService as Service diff --git a/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_bolted/adapter.py b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_bolted/adapter.py new file mode 100644 index 000000000..67d01d382 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_bolted/adapter.py @@ -0,0 +1,415 @@ +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl, +) +from ...shared import setup_for_cad # Use moment_connection shared utilities +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from osdag_core.cad.common_logic import CommonDesignLogic +# Will log a lot of unnessecary data. +from osdag_core.design_type.connection.column_cover_plate import ColumnCoverPlate +import sys +import os +from typing import Dict, Any, List +import traceback + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + return [ + "Bolt.Bolt_Hole_Type", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Slip_Factor", + "Bolt.TensionType", + "Bolt.Type", + "Connector.Flange_Plate.Preferences", + "Connector.Flange_Plate.Thickness_list", + "Connector.Material", + "Connector.Web_Plate.Thickness_List", + "Design.Design_Method", + "Detailing.Corrosive_Influences", + "Detailing.Edge_type", + "Detailing.Gap", + "Load.Axial", + "Load.Moment", + "Load.Shear", + "Material", + "Member.Designation", + "Member.Material", + "Module", + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + raise MissingKeyError(missing_keys[0]) + + # Validate key types one by one: + + # Validate Bolt.Bolt_Hole_Type. + # Check if Bolt.Bolt_Hole_Type is a string. + if not isinstance(input_values["Bolt.Bolt_Hole_Type"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type", "str") + + # Validate Bolt.Diameter. + bolt_diameter = input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter, list) # Check if Bolt.Diameter is a list. + # Check if all items in Bolt.Diameter are str. + or not validate_list_type(bolt_diameter, str) + or not custom_list_validation(bolt_diameter, int_able)): # Check if all items in Bolt.Diameter can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Diameter", "non empty List[str] where all items can be converted to int") + + # Validate Bolt.Grade + bolt_grade = input_values["Bolt.Grade"] + if (not isinstance(bolt_grade, list) # Check if Bolt.Grade is a list. + # Check if all items in Bolt.Grade are str. + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): # Check if all items in Bolt.Grade can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Grade", "non empty List[str] where all items can be converted to float") + + # Validate Bolt.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor, str) # Check if Bolt.Slip_Factor is a string. + or not float_able(bolt_slipfactor)): # Check if Bolt.Slip_Factor can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Slip_Factor", "str where str can be converted to float") + + # Validate Bolt.TensionType + # Check if Bolt.TensionType is a string. + if not isinstance(input_values["Bolt.TensionType"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.TensionType", "str") + + # Validate Bolt.Type + # Check if Bolt.Type is a string. + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") # If not, raise error. + + # Validate Connector.Flange_Plate.Preferences + if not isinstance(input_values["Connector.Flange_Plate.Preferences"], str): + raise InvalidInputTypeError("Connector.Flange_Plate.Preferences", "str") + + # Validate Connector.Flange_Plate.Thickness_list + flange_thickness_list = input_values["Connector.Flange_Plate.Thickness_list"] + if (not isinstance(flange_thickness_list, list) + or not validate_list_type(flange_thickness_list, str) + or not custom_list_validation(flange_thickness_list, int_able)): + raise InvalidInputTypeError( + "Connector.Flange_Plate.Thickness_list", "List[str] where all items can be converted to int") + # Validate Connector.Material + # Check if Connector.Material is a string. + if not isinstance(input_values["Connector.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Connector.Web_Plate.Thickness_List + web_thickness_list = input_values["Connector.Web_Plate.Thickness_List"] + if (not isinstance(web_thickness_list, list) + or not validate_list_type(web_thickness_list, str) + or not custom_list_validation(web_thickness_list, int_able)): + raise InvalidInputTypeError( + "Connector.Web_Plate.Thickness_List", "List[str] where all items can be converted to int") + + # Validate Design.Design_Method + # Check if Design.Design_Method is a string. + if not isinstance(input_values["Design.Design_Method"], str): + # If not, raise error. + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + # Check if Detailing.Corrosive_Influences is 'Yes' or 'No'. + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + # If not, raise error. + raise InvalidInputTypeError( + "Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + # Check if Detailing.Edge_type is a string. + if not isinstance(input_values["Detailing.Edge_type"], str): + # If not, raise error. + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) # Check if Detailing.Gap is a string. + or not int_able(detailing_gap)): # Check if Detailing.Gap can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) # Check if Load.Axial is a string. + or not int_able(load_axial)): # Check if Load.Axial can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Axial", "str where str can be converted to int") + + # Validate Load.Moment + load_moment = input_values["Load.Moment"] + if not isinstance(load_moment, str) or not int_able(load_moment): + raise InvalidInputTypeError("Load.Moment", "str where str can be converted to int") + + # Validate Load.Shear + load_shear = input_values["Load.Shear"] + if (not isinstance(load_shear, str) # Check if Load.Shear is a string. + or not int_able(load_shear)): # Check if Load.Shear can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Shear", "str where str can be converted to int") + + # Validate Material + # Check if Material is a string. + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") # If not, raise error. + + # Validate Member.Designation + if not isinstance(input_values["Member.Designation"], str): + raise InvalidInputTypeError("Member.Designation", "str") + + # Validate Member.Material + if not isinstance(input_values["Member.Material"], str): + raise InvalidInputTypeError("Member.Material", "str") + + # Check if Module is a string. + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") # If not, raise error. + +def create_module() -> ColumnCoverPlate: + """Create an instance of the ColumnCoverPlate module design class and set it up for use""" + module = ColumnCoverPlate() # Create an instance of the ColumnCoverPlate + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> ColumnCoverPlate: + """Create an instance of the ColumnCoverPlate module design class from input values.""" + try : + module = create_module() # Create module instance. + except Exception as e : + print('e in create_module : ' , e) + print('error in creating module') + + # Set the input values on the module instance. + try : + print("INPUT SET FOR FINAL OUTPUT",input_values) + module.set_input_values(input_values) + except Exception as e : + traceback.print_exc() + print('e in set_input_values : ' , e) + print('error in setting the input values') + + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """ + Generate, format and return the input values from the given output values. + Output format (json): { + "Bolt.Pitch": + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)" + "value": 40 + } + } + """ + print("************") + output = {} # Dictionary for formatted values + module = create_from_input(input_values) # Create module from input. + print('module : ' , module) + print('type of module : ' , type(module)) + + # Generate output values in unformatted form. + raw_output_text = module.output_values(True) + raw_member_capacity = module.member_capacityoutput(True) + raw_flange_bolt_capacity = [ + (f"{key}_flange_bolt_capacity", label, typ, value, visible if len(item) == 5 else True) + for item in module.flange_bolt_capacity(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_web_bolt_capacity = [ + (f"{key}_web_bolt_capacity", label, typ, value, visible if len(item) == 5 else True) + for item in module.web_bolt_capacity(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_flange_capacity = [ + (f"{key}_flange_capacity", label, typ, value, visible if len(item) == 5 else True) + for item in module.flangecapacity(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_web_capacity = [ + (f"{key}_web_capacity", label, typ, value, visible if len(item) == 5 else True) + for item in module.webcapacity(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_flange_spacing = [ + (f"{key}_flange_spacing", label, typ, value, visible if len(item) == 5 else True) + for item in module.flangespacing(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_web_spacing = [ + (f"{key}_web_spacing", label, typ, value, visible if len(item) == 5 else True) + for item in module.webspacing(True) + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + from osdag_core.custom_logger import CustomLogger + if hasattr(module, "logger") and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() or [] + else: + logs = getattr(module, "logs", []) or [] + raw_output = ( + raw_output_text + + raw_member_capacity + + raw_flange_bolt_capacity + + raw_web_bolt_capacity + + raw_flange_capacity + + raw_web_capacity + + raw_flange_spacing + + raw_web_spacing + ) + # os.system("clear") + # Loop over all the text values and add them to ouptut dict. + for param in raw_output: + if param[2] == "TextBox": # If the parameter is a text output, + key = param[0] # id/key + label = param[1] # label text. + value = param[3] # Value as string. + output[key] = { + "key": key, + "label": label, + "val": value # Changed from "value" to "val" to match frontend expectations + } # Set label, key and value in output + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP/STL file. + + External API uses section names: "Model", "Column", "CoverPlate". + Internally, the legacy CAD logic for column cover plate uses component + name "Connector" for the cover plate + bolts assembly. Map CoverPlate -> + Connector for CAD routing, but keep the external section name for file + naming and response keys. + """ + if section == "Plate": + section = "CoverPlate" + if section not in ("Model", "Column", "CoverPlate"): + raise InvalidInputTypeError("section", "'Model', 'Column' or 'CoverPlate'") + + module = create_from_input(input_values) + from osdag_core.Common import KEY_DISP_COLUMNCOVERPLATE + if getattr(module, "module", None) != KEY_DISP_COLUMNCOVERPLATE: + print(f"[CAD DEBUG] Adjusting module.module from {getattr(module,'module',None)} to {KEY_DISP_COLUMNCOVERPLATE}") + module.module = KEY_DISP_COLUMNCOVERPLATE + if getattr(module, "mainmodule", None) != "Moment Connection": + print(f"[CAD DEBUG] Adjusting module.mainmodule from {getattr(module,'mainmodule',None)} to Moment Connection") + module.mainmodule = "Moment Connection" + + print(f"[CAD DEBUG] building CommonDesignLogic with module={module.module}, mainmodule={module.mainmodule}, section={section}") + # CommonDesignLogic(display, cad_widget, folder, connection, mainmodule) + cld = CommonDesignLogic(None, "", "", module.module, module.mainmodule) + setup_for_cad(cld, module) + + # Map external section names to internal component names expected by CommonDesignLogic. + # For column cover plate, the core CAD logic expects component name "Cover Plate" + # (with a space) for the plate + bolts assembly. Using "Connector" here falls + # through to CPObj.get_models(), which can later be treated like a list and + # cause TypeErrors such as "object of type 'TopoDS_Compound' has no len()". + internal_section = section + if section == "CoverPlate": + internal_section = "Cover Plate" + + cld.component = internal_section + print(f"[cadissue] CC cover plate bolted: cld.component set to {internal_section} for section={section}") + model = cld.create2Dcad() + + # check if the cad_models folder exists or not + # if no, then create one + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + + print('2d model : ' , model) + # os.system("clear") # clear the terminal + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + print('brep file path in create_cad_model : ' , file_path) + + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) # Generate CAD Model + + # Always try to write STL for the requested section + try: + stl_rel = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_rel) + write_stl(model, full_stl) + print(f"STL file saved at {full_stl}") + except Exception as stle: + print(f"Warning: Failed to save STL at {file_path}: {stle}") + + if section == "Model": + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + + except Exception as e: + print('Writing to BREP/STL file failed e : ', e) + + return file_path + diff --git a/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_bolted/service.py b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_bolted/service.py new file mode 100644 index 000000000..9fee54500 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_bolted/service.py @@ -0,0 +1,31 @@ +""" +Column Cover Plate Bolted Service - Business logic layer +Bridges between API and osdag_core +""" +from osdag_core.design_type.connection.column_cover_plate import ColumnCoverPlate +from .adapter import validate_input, generate_output, create_cad_model + + +class ColumnCoverPlateBoltedService: + """Service class for Column Cover Plate Bolted Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + validate_input(inputs) + model = ColumnCoverPlate() + if hasattr(model, "set_osdaglogger"): + model.set_osdaglogger(None, id="web") + model.set_input_values(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs, + 'success': True + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_welded/__init__.py b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_welded/__init__.py new file mode 100644 index 000000000..74a1da74c --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_welded/__init__.py @@ -0,0 +1,6 @@ +""" +Column Cover Plate Welded Connection Sub-module +""" +MODULE_ID = 'Column-to-Column-Cover-Plate-Welded-Connection' +from .service import ColumnCoverPlateWeldedService as Service + diff --git a/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_welded/adapter.py b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_welded/adapter.py new file mode 100644 index 000000000..ff046c434 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_welded/adapter.py @@ -0,0 +1,300 @@ +""" +Api for Column Cover Plate Welded Connection module +Functions: + get_required_keys() -> List[str]: + Return all required input parameters for the module. + validate_input(input_values: Dict[str, Any]) -> None: + Go through all the input parameters. + Check if all required parameters are given. + Check if all parameters are of correct data type. + create_module() -> ColumnCoverPlateWeld: + Create an instance of the column cover plate welded connection module design class and set it up for use + create_from_input(input_values: Dict[str, Any]) -> ColumnCoverPlateWeld + Create an instance of the column cover plate welded connection module design class from input values. + generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + Generate, format and return the output values from the given input values. + create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + Generate the CAD model from input values as a BREP file. Return file path. +""" + +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl, +) + +from ...shared import setup_for_cad # Use moment_connection shared utilities +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.design_type.connection.column_cover_plate_weld import ColumnCoverPlateWeld +import sys +import os +import typing +from typing import Dict, Any, List +import traceback + +old_stdout = sys.stdout +sys.stdout = open(os.devnull, "w") +sys.stdout = old_stdout + + +def get_required_keys() -> List[str]: + return [ + "Module", + "Member.Designation", + "Member.Material", + "Material", + "Load.Moment", + "Load.Shear", + "Load.Axial", + "Weld.Type", + "Weld.Fab", + "Weld.Material_Grade_OverWrite", + "Design.Design_Method", + "Detailing.Gap", + "Connector.Material", + "Connector.Flange_Plate.Preferences", + "Connector.Flange_Plate.Thickness_list", + "Connector.Web_Plate.Thickness_List" + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + try: + required_keys = get_required_keys() + + # Filter out UI-specific keys that are not required by the backend module + filtered_input = {k: v for k, v in input_values.items() if k != "out_titles_status"} + + missing_keys = contains_keys(filtered_input, required_keys) + if missing_keys != None: + raise MissingKeyError(f"Required key '{missing_keys[0]}' is missing from input") + + # Validate string fields + string_fields = [ + "Module", + "Member.Designation", + "Member.Material", + "Material", + "Weld.Type", + "Weld.Fab", + "Design.Design_Method", + "Connector.Material", + "Connector.Flange_Plate.Preferences" + ] + for key in string_fields: + if key in filtered_input and not isinstance(filtered_input[key], str): + raise InvalidInputTypeError( + f"Field '{key}' must be a string, got {type(filtered_input[key]).__name__}" + ) + + # Validate numeric fields + numeric_fields = [ + "Load.Moment", + "Load.Shear", + "Load.Axial", + "Weld.Material_Grade_OverWrite", + "Detailing.Gap" + ] + for key in numeric_fields: + if key in filtered_input: + value = filtered_input[key] + if not isinstance(value, str) or not float_able(value): + raise InvalidInputTypeError( + f"Field '{key}' must be a string that can be converted to float, got '{value}'" + ) + # Additional numeric validation + float_val = float(value) + if key == "Detailing.Gap" and float_val < 0: + raise ValueError(f"Gap value must be non-negative, got {float_val}") + + # Validate plate thickness lists + thickness_lists = [ + "Connector.Flange_Plate.Thickness_list", + "Connector.Web_Plate.Thickness_List" + ] + for key in thickness_lists: + if key in filtered_input: + thickness = filtered_input[key] + if not isinstance(thickness, list): + raise InvalidInputTypeError( + f"Field '{key}' must be a list, got {type(thickness).__name__}" + ) + if not validate_list_type(thickness, str): + raise InvalidInputTypeError( + f"All items in '{key}' must be strings" + ) + if not custom_list_validation(thickness, float_able): + invalid_items = [x for x in thickness if not float_able(x)] + raise InvalidInputTypeError( + f"All items in '{key}' must be convertible to float. Invalid items: {invalid_items}" + ) + + except Exception as e: + raise type(e)(f"Input validation failed: {str(e)}") + + +def create_module() -> ColumnCoverPlateWeld: + """Create an instance of the column cover plate welded connection module design class and set it up for use""" + module = ColumnCoverPlateWeld() + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> ColumnCoverPlateWeld: + """Create an instance of the column cover plate welded connection module design class from input values.""" + module = create_module() + module.set_input_values(input_values) + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate, format and return formatted output""" + output = {} + module = create_from_input(input_values) + + # Get raw output data + raw_output_text = module.output_values(True) + raw_member_capacity = module.member_capacityoutput(True) + flange_weld_details = module.flange_weld_details(True) + web_weld_details = module.web_weld_details(True) + web_capacity = module.webcapacity(True) + flange_capacity = module.flangecapacity(True) + web_block_shear_pattern = module.web_pattern(True) + + from osdag_core.custom_logger import CustomLogger + if hasattr(module, "logger") and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() or [] + else: + logs = getattr(module, "logs", []) or [] + raw_output = ( + raw_output_text + + raw_member_capacity + + flange_weld_details + + web_weld_details + + web_capacity + + flange_capacity + + web_block_shear_pattern + ) + + # Format output + for param in raw_output: + if param[2] == "TextBox": + key = param[0] + label = param[1] + value = param[3] + output[key] = { + "key": key, + "label": label, + "val": value + } + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP/STL file. + + External API uses section names: "Model", "Column", "CoverPlate". + Internally, the legacy CAD logic for column cover plate welded uses + component name "Connector" for the cover plate + bolts assembly. Map + CoverPlate -> Connector for CAD routing, but keep the external section + name for file naming and response keys. + """ + if section not in ("Model", "Column", "CoverPlate"): # Error checking: If section is valid. + raise InvalidInputTypeError( + "section", "'Model', 'Column' or 'CoverPlate'") + module = create_from_input(input_values) # Create module from input. + + # Ensure correct display keys for CAD routing + from osdag_core.Common import KEY_DISP_COLUMNCOVERPLATEWELD + if getattr(module, "module", None) != KEY_DISP_COLUMNCOVERPLATEWELD: + print(f"[CAD DEBUG] Adjusting module.module from {getattr(module,'module',None)} to {KEY_DISP_COLUMNCOVERPLATEWELD}") + module.module = KEY_DISP_COLUMNCOVERPLATEWELD + if getattr(module, "mainmodule", None) != "Moment Connection": + print(f"[CAD DEBUG] Adjusting module.mainmodule from {getattr(module,'mainmodule',None)} to Moment Connection") + module.mainmodule = "Moment Connection" + + print(f"[CAD DEBUG] building CommonDesignLogic with module={module.module}, mainmodule={module.mainmodule}, section={section}") + # Object that will create the CAD model. + try: + # CommonDesignLogic(display, cad_widget, folder, connection, mainmodule) + cld = CommonDesignLogic(None, '', "", module.module , module.mainmodule) + except Exception as e : + print('error in cld e : ' , e) + + try: + # Setup the calculations object for generating CAD model. + setup_for_cad(cld, module) + except Exception as e: + traceback.print_exc() + print('Error in setting up cad e : ' , e) + + # The section of the module that will be generated. + internal_section = section + if section == "CoverPlate": + # CommonDesignLogic expects "Cover Plate" (with space) for cover plate + # + welds assembly in the column cover plate welded branch. + internal_section = "Cover Plate" + + cld.component = internal_section + print(f"[cadissue] CC cover plate welded: cld.component set to {internal_section} for section={section}") + + try: + model = cld.create2Dcad() # Generate CAD Model. + except Exception as e: + print('Error in cld.create2Dcad() e : ' , e) + return False + + # check if the cad_models folder exists or not + # if no, then create one + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) # Generate CAD Model + + # Always try to write STL for the requested section + try: + stl_rel = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_rel) + write_stl(model, full_stl) + print(f"STL file saved at {full_stl}") + except Exception as stle: + print(f"Warning: Failed to save STL at {file_path}: {stle}") + + if section == "Model": + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + + except Exception as e: + print('Writing to BREP/STL file failed e : ', e) + + return file_path + diff --git a/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_welded/service.py b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_welded/service.py new file mode 100644 index 000000000..905e110fc --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/column_column_cover_plate_welded/service.py @@ -0,0 +1,31 @@ +""" +Column Cover Plate Welded Service - Business logic layer +Bridges between API and osdag_core +""" +from osdag_core.design_type.connection.column_cover_plate_weld import ColumnCoverPlateWeld +from .adapter import validate_input, generate_output, create_cad_model + + +class ColumnCoverPlateWeldedService: + """Service class for Column Cover Plate Welded Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + validate_input(inputs) + model = ColumnCoverPlateWeld() + if hasattr(model, "set_osdaglogger"): + model.set_osdaglogger(None, id="web") + model.set_input_values(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs, + 'success': True + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/moment_connection/submodules/column_column_end_plate/__init__.py b/backend/apps/modules/moment_connection/submodules/column_column_end_plate/__init__.py new file mode 100644 index 000000000..866eaec8a --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/column_column_end_plate/__init__.py @@ -0,0 +1,6 @@ +""" +Column End Plate Connection Sub-module +""" +MODULE_ID = 'Column-to-Column-End-Plate-Connection' +from .service import ColumnEndPlateService as Service + diff --git a/backend/apps/modules/moment_connection/submodules/column_column_end_plate/adapter.py b/backend/apps/modules/moment_connection/submodules/column_column_end_plate/adapter.py new file mode 100644 index 000000000..e0561fed9 --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/column_column_end_plate/adapter.py @@ -0,0 +1,484 @@ +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl +) +from ...shared import setup_for_cad # Use moment_connection shared utilities +from osdag_core.cad.common_logic import CommonDesignLogic +from OCC.Core import BRepTools +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from osdag_core.custom_logger import CustomLogger +from osdag_core.design_type.connection.column_end_plate import ColumnEndPlate +import sys +import os +import typing +import traceback +import logging +import json +from typing import Dict, Any, List + +# Configure logger +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) +# Create handlers +c_handler = logging.StreamHandler() +f_handler = logging.FileHandler('column_end_plate_module.log') +c_handler.setLevel(logging.INFO) +f_handler.setLevel(logging.DEBUG) +# Create formatters and add it to handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +c_handler.setFormatter(formatter) +f_handler.setFormatter(formatter) +# Add handlers to the logger +logger.addHandler(c_handler) +logger.addHandler(f_handler) + +# Suppress output when necessary +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + return [ + "Bolt.Bolt_Hole_Type", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Slip_Factor", + "Bolt.TensionType", + "Bolt.Type", + "Connectivity", + "Connector.Material", + "Design.Design_Method", + "Detailing.Corrosive_Influences", + "Detailing.Edge_type", + "Detailing.Gap", + "Load.Axial", + "Load.Shear", + "Load.Moment", + "Material", + "Member.Designation", + "Member.Material", + "Module", + "Weld.Fab", + "Weld.Material_Grade_OverWrite", + "Weld.Type", + "Connector.Plate.Thickness_List", + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + raise MissingKeyError(missing_keys[0]) + + # Validate key types one by one: + + # Validate Bolt.Bolt_Hole_Type. + if not isinstance(input_values["Bolt.Bolt_Hole_Type"], str): + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type", "str") + + # Validate Bolt.Diameter. + bolt_diameter = input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter, list) + or not validate_list_type(bolt_diameter, str) + or not custom_list_validation(bolt_diameter, int_able)): + raise InvalidInputTypeError( + "Bolt.Diameter", "non empty List[str] where all items can be converted to int") + + # Validate Bolt.Grade + bolt_grade = input_values["Bolt.Grade"] + if (not isinstance(bolt_grade, list) + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): + raise InvalidInputTypeError( + "Bolt.Grade", "non empty List[str] where all items can be converted to float") + + # Validate Bolt.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor, str) + or not float_able(bolt_slipfactor)): + raise InvalidInputTypeError( + "Bolt.Slip_Factor", "str where str can be converted to float") + + # Validate Bolt.TensionType + if not isinstance(input_values["Bolt.TensionType"], str): + raise InvalidInputTypeError("Bolt.TensionType", "str") + + # Validate Bolt.Type + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") + + # Validate Connectivity + if not isinstance(input_values["Connectivity"], str): + raise InvalidInputTypeError("Connectivity", "str") + + + # Validate Connector.Material + if not isinstance(input_values["Connector.Material"], str): + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + if not isinstance(input_values["Design.Design_Method"], str): + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + raise InvalidInputTypeError( + "Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + if not isinstance(input_values["Detailing.Edge_type"], str): + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) + or not int_able(detailing_gap)): + raise InvalidInputTypeError( + "Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) + or not int_able(load_axial)): + raise InvalidInputTypeError( + "Load.Axial", "str where str can be converted to int") + + # Validate Load.Shear + load_shear = input_values["Load.Shear"] + if (not isinstance(load_shear, str) + or not int_able(load_shear)): + raise InvalidInputTypeError( + "Load.Shear", "str where str can be converted to int") + + # Validate Load.Moment + load_moment = input_values["Load.Moment"] + if (not isinstance(load_moment, str) + or not int_able(load_moment)): + raise InvalidInputTypeError( + "Load.Moment", "str where str can be converted to int") + + # Validate Material + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") + + # Validate Member.Designation + if not isinstance(input_values["Member.Designation"], str): + raise InvalidInputTypeError("Member.Designation", "str") + + # Validate Member.Material + if not isinstance(input_values["Member.Material"], str): + raise InvalidInputTypeError("Member.Material", "str") + + # Validate Module + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") + + # Validate Weld.Fab + if not isinstance(input_values["Weld.Fab"], str): + raise InvalidInputTypeError("Weld.Fab", "str") + + # Validate Weld.Type + if not isinstance(input_values["Weld.Type"], str): + raise InvalidInputTypeError("Weld.Type", "str") + + # Validate Weld.Material_Grade_OverWrite + weld_materialgradeoverwrite = input_values["Weld.Material_Grade_OverWrite"] + if (not isinstance(weld_materialgradeoverwrite, str) + or not int_able(weld_materialgradeoverwrite)): + raise InvalidInputTypeError( + "Weld.Material_Grade_OverWrite", "str where str can be converted to int.") + + # Validate Connector.Plate.Thickness_List + connector_plate_thicknesslist = input_values["Connector.Plate.Thickness_List"] + if (not isinstance(connector_plate_thicknesslist, list) + or not validate_list_type(connector_plate_thicknesslist, str) + or not custom_list_validation(connector_plate_thicknesslist, int_able)): + raise InvalidInputTypeError( + "Connector.Plate.Thickness_List", "List[str] where all items can be converted to int") + + +def create_module() -> ColumnEndPlate: + """Create an instance of the Column End Plate connection module design class and set it up for use""" + logger.info("Creating ColumnEndPlate module instance") + try: + module = ColumnEndPlate() + logger.debug("ColumnEndPlate instance created successfully") + module.set_osdaglogger(None, id="web") + logger.debug("OSDAGLogger set to None") + return module + except Exception as e: + logger.error(f"Error creating ColumnEndPlate module: {str(e)}") + logger.error(traceback.format_exc()) + raise + + +def create_from_input(input_values: Dict[str, Any]) -> ColumnEndPlate: + """Create an instance of the Column End Plate connection module design class from input values.""" + logger.info("Creating module from input values") + + # Validate input values first to catch issues early + try: + logger.debug("Validating input values") + validate_input(input_values) + logger.debug("Input validation successful") + except Exception as e: + logger.error(f"Input validation failed: {str(e)}") + logger.error(traceback.format_exc()) + raise + + try: + logger.debug("Creating module instance") + module = create_module() + logger.debug("Module instance created successfully") + except Exception as e: + logger.error(f"Error in create_module: {str(e)}") + logger.error(traceback.format_exc()) + raise + + # Set the input values on the module instance. + try: + logger.debug("Setting input values on module instance") + logger.debug(f"Input values: {input_values}") + module.set_input_values(input_values) + logger.debug("Input values set successfully") + except Exception as e: + logger.error(f"Error in set_input_values: {str(e)}") + logger.error(traceback.format_exc()) + raise + + logger.info("Module created and initialized with input values successfully") + return module + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate, format and return formatted output""" + logger.info("Generating output for Column-to-Column End Plate") + output = {} + logs = [] # Initialize logs + + try: + module = create_from_input(input_values) + raw_output_text = module.output_values(True) + raw_output_flange = module.flange_bolt_spacing(True) + raw_output_web = module.web_bolt_spacing(True) + + if hasattr(module, 'logger') and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() or [] + print(f'Retrieved {len(logs)} logs from custom logger') + else: + print('Logger is not CustomLogger instance or logger not found') + print(f'Logger type: {type(module.logger) if hasattr(module, "logger") else "No logger"}') + logs = getattr(module, "logs", []) or [] + + raw_output = ( + raw_output_text + + raw_output_flange + raw_output_web + ) + + # Format output + for param in raw_output: + if len(param) >= 4 and param[2] == "TextBox": + key = param[0] + label = param[1] + value = param[3] + + # Handle numpy types + if hasattr(value, 'item'): + value = value.item() + + output[key] = { + "key": key, + "label": label, + "val": value + } + + logger.info(f"Output generation completed. Generated {len(output)} output fields and {len(logs)} log messages") + + except Exception as e: + logger.error(f"Error in generate_output: {str(e)}") + logger.error(traceback.format_exc()) + # Return what we have so far + logger.debug(f"Final logs being returned: {logs}") + return output, logs + + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + if section not in ("Model", "Column", "Connector"): # Error checking: If section is valid. + raise InvalidInputTypeError( + "section", "'Model', 'Column' or 'Connector'") + module = create_from_input(input_values) # Create module from input. + + # Ensure correct display keys for CAD routing + from osdag_core.Common import KEY_DISP_COLUMNENDPLATE + if getattr(module, "module", None) != KEY_DISP_COLUMNENDPLATE: + print(f"[CAD DEBUG] Adjusting module.module from {getattr(module,'module',None)} to {KEY_DISP_COLUMNENDPLATE}") + module.module = KEY_DISP_COLUMNENDPLATE + if getattr(module, "mainmodule", None) != "Moment Connection": + print(f"[CAD DEBUG] Adjusting module.mainmodule from {getattr(module,'mainmodule',None)} to Moment Connection") + module.mainmodule = "Moment Connection" + + print(f"[CAD DEBUG] building CommonDesignLogic with module={module.module}, mainmodule={module.mainmodule}, section={section}") + # Object that will create the CAD model. + try: + # CommonDesignLogic(display, cad_widget, folder, connection, mainmodule) + cld = CommonDesignLogic(None, '', "", module.module , module.mainmodule) + except Exception as e : + print('error in cld e : ' , e) + + try : + # Setup the calculations object for generating CAD model. + setup_for_cad(cld, module) + except Exception as e : + traceback.print_exc() + print('Error in setting up cad e : ' , e) + + # The section of the module that will be generated. + cld.component = section + + # When section == "Model", also ensure per-part shapes exist and prepare a compound + part_names = ["Column", "Connector"] + part_files = {} + compound_model = None + + try: + if section == "Model": + # Build compound by adding each part shape without fusing + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + + for part in part_names: + try: + # Generate shape for this part + cld.component = part + part_shape = cld.create2Dcad() + if part_shape is None: + continue + + # Add to compound + builder.Add(compound, part_shape) + + # Ensure per-part BREP file exists (write or overwrite) + part_file_name = f"{session}_{part}.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + BRepTools.breptools.Write(part_shape, part_file_path_rel, Message_ProgressRange()) + part_files[part] = part_file_path_rel + # Also write STL for this part + try: + part_stl_rel = part_file_path_rel.replace(".brep", ".stl") + write_stl(part_shape, os.path.join(os.getcwd(), part_stl_rel)) + except Exception as stle: + print(f"Failed to write STL for part {part}: {stle}") + except Exception as e: + print(f"Failed to build/write part {part}: {e}") + + # Reset component to Model and set compound as the model to write + cld.component = section + compound_model = compound + # Generate model for non-Model sections (or fallback) + if compound_model is not None: + model = compound_model + else: + model = cld.create2Dcad() + except Exception as e : + print('Error in cld.create2Dcad() e : ' , e) + return False + + # check if the cad_models folder exists or not + # if no, then create one + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + + try : + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) # Generate CAD Model + + # Always try to write STL for the requested section + try: + stl_rel = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_rel) + write_stl(model, full_stl) + print(f"STL file saved at {full_stl}") + except Exception as stle: + print(f"Warning: Failed to save STL at {file_path}: {stle}") + + # If it's 'Model' section, write a manifest referencing per-part breps and save extra formats + if section == "Model": + try: + manifest = { + "session": session, + "mergedBrep": file_path, + "parts": [ + {"name": name, "brepPath": part_files.get(name)} for name in part_names if part_files.get(name) + ] + } + # add stlPath for parts + for entry in manifest["parts"]: + if entry.get("brepPath"): + entry["stlPath"] = entry["brepPath"].replace(".brep", ".stl") + manifest_path = file_path.replace(".brep", ".parts.json") + full_manifest_path = os.path.join(os.getcwd(), manifest_path) + with open(full_manifest_path, "w", encoding="utf-8") as mf: + json.dump(manifest, mf) + print(f"Parts manifest saved at {full_manifest_path}") + except Exception as me: + print(f"Warning: Failed to write manifest: {me}") + + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + # Write merged STL for Model + try: + merged_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), merged_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), merged_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save merged STL: {stle}") + except Exception as e : + print('Writing to BREP file failed e : ' , e) + + # For non-Model sections, export single STL next to BREP + if section != "Model": + try: + single_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), single_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save STL for {section}: {stle}") + + return file_path + diff --git a/backend/apps/modules/moment_connection/submodules/column_column_end_plate/service.py b/backend/apps/modules/moment_connection/submodules/column_column_end_plate/service.py new file mode 100644 index 000000000..7c50df25c --- /dev/null +++ b/backend/apps/modules/moment_connection/submodules/column_column_end_plate/service.py @@ -0,0 +1,31 @@ +""" +Column End Plate Service - Business logic layer +Bridges between API and osdag_core +""" +from osdag_core.design_type.connection.column_end_plate import ColumnEndPlate +from .adapter import validate_input, generate_output, create_cad_model + + +class ColumnEndPlateService: + """Service class for Column End Plate Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + validate_input(inputs) + model = ColumnEndPlate() + if hasattr(model, "set_osdaglogger"): + model.set_osdaglogger(None, id="web") + model.set_input_values(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs, + 'success': True + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/moment_connection/urls.py b/backend/apps/modules/moment_connection/urls.py new file mode 100644 index 000000000..0a9cc1789 --- /dev/null +++ b/backend/apps/modules/moment_connection/urls.py @@ -0,0 +1,12 @@ +""" +Moment Connection URLs +""" +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import MomentConnectionViewSet + +router = DefaultRouter() +router.register(r'', MomentConnectionViewSet, basename='moment-connection') + +urlpatterns = router.urls + diff --git a/backend/apps/modules/moment_connection/views.py b/backend/apps/modules/moment_connection/views.py new file mode 100644 index 000000000..f043044b5 --- /dev/null +++ b/backend/apps/modules/moment_connection/views.py @@ -0,0 +1,389 @@ +""" +Moment Connection ViewSet - Routes to sub-module services +Uses URL slug (not POST body) to find the correct service +Handles guest mode and optional project_id saving +""" +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from rest_framework import status +from .registry import MomentConnectionRegistry +from apps.core.utils.module_helpers import handle_design_request +from apps.core.utils.cad_helpers import generate_cad_models, get_default_sections +from apps.core.models import Columns, Beams, Bolt, Material, CustomMaterials +from apps.core.api.design.report_customization_api import generate_initial_report_core + + +# Mapping from moment-connection submodule slug to legacy report module_id +MOMENT_REPORT_MODULE_ID_MAP = { + "beam-beam-cover-plate-bolted": "Cover-Plate-Bolted-Connection", + "beam-beam-cover-plate-welded": "Cover-Plate-Welded-Connection", + "beam-beam-end-plate": "Beam-Beam-End-Plate-Connection", + "beam-column-end-plate": "Beam-to-Column-End-Plate-Connection", + "column-column-cover-plate-bolted": "ColumnCoverPlateBolted", + "column-column-cover-plate-welded": "Column-to-Column-Cover-Plate-Welded-Connection", + "column-column-end-plate": "Column-to-Column-End-Plate-Connection", +} + + +class MomentConnectionViewSet(viewsets.ViewSet): + """ + Generic ViewSet that routes to specific sub-module services based on URL slug. + Supports guest mode and optional project_id saving for authenticated users. + """ + permission_classes = [AllowAny] # Allow both authenticated and guest users + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/design') + def design(self, request, submodule_slug=None): + """ + POST /api/modules/moment-connection/{submodule_slug}/design/ + + Request body: + { + "inputs": {...}, # Design input parameters + "project_id": 123 # Optional: Save results to project if user is authenticated + } + + Example: POST /api/modules/moment-connection/cover-plate-bolted/design/ + + Guest Mode: + - Can calculate designs + - Cannot save to projects (project_id is ignored) + + Authenticated Users: + - Can calculate designs + - Can save to projects if project_id is provided + """ + # Use URL slug to find service (not POST body) + ServiceClass = MomentConnectionRegistry.get_service_by_slug(submodule_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {submodule_slug} not found'}, + status=404 + ) + + # Extract inputs and optional project_id + inputs = request.data.get('inputs', request.data) # Support both formats + project_id = request.data.get('project_id') + + # Handle authentication and project saving (shared logic) + context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug=submodule_slug, + module_name='moment-connection' + ) + + try: + # Call the service with request context + result = ServiceClass.calculate( + inputs=inputs, + request=request, + project_id=project_id if not context['is_guest'] else None, + user_email=context['user_email'] + ) + + # Add project saving result to response + if context['project_result']: + result['project_saved'] = context['project_result']['saved'] + if context['project_result'].get('project_id'): + result['project_id'] = context['project_result']['project_id'] + if context['project_result'].get('error'): + result['project_error'] = context['project_result']['error'] + + return Response(result, status=200) + except Exception as e: + return Response( + {'error': str(e), 'success': False}, + status=400 + ) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/report/generate-initial') + def report_generate_initial(self, request, submodule_slug=None): + """ + POST /api/modules/moment-connection/{submodule_slug}/report/generate-initial/ + + Request body: + { + "metadata": {...}, + "input_values": {...}, # Or "inputs": {...} + "design_status": boolean, + "logs": [...], + "sections": [...], # Optional + "customization": {...} # Optional + } + """ + module_id = MOMENT_REPORT_MODULE_ID_MAP.get(submodule_slug) + if not module_id: + return Response( + { + "success": False, + "error": f"Report generation is not supported for moment-connection sub-module '{submodule_slug}'", + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + input_values = request.data.get("input_values") or request.data.get("inputs") + if not input_values: + return Response( + {"success": False, "error": "input_values are required"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + mapped_data = { + "module_id": module_id, + "input_values": input_values, + "metadata": request.data.get("metadata"), + "design_status": request.data.get("design_status", True), + "logs": request.data.get("logs", []), + } + + if "sections" in request.data: + mapped_data["sections"] = request.data.get("sections") + if "customization" in request.data: + mapped_data["customization"] = request.data.get("customization") + + payload, status_code = generate_initial_report_core(mapped_data) + return Response(payload, status=status_code) + + @action(detail=False, methods=['get'], url_path='(?P[^/.]+)/options') + def options(self, request, submodule_slug=None): + """ + GET /api/modules/moment-connection/{submodule_slug}/options/ + + Returns dropdown/options data for the sub-module. + """ + email = request.query_params.get("email") + slug = submodule_slug + + # Common data helpers + def material_list(): + mats = list(Material.objects.all().values()) + if email: + mats += list(CustomMaterials.objects.filter(email=email).values()) + mats.append({"id": -1, "Grade": "Custom"}) + return mats + + def bolt_diameters(): + lst = list(Bolt.objects.values_list('Bolt_diameter', flat=True)) + lst.sort() + return lst + + property_classes = ['3.6', '4.6', '4.8', '5.6', '5.8', '6.8', '8.8', '9.8', '10.9', '12.9'] + thickness_list = [ + '8', '10', '12', '14', '16', '18', '20', '22', '25', '28', '32', '36', '40', '45', '50', + '56', '63', '75', '80', '90', '100', '110', '120' + ] + + try: + # Beam-to-Beam Cover Plate Bolted + if slug == 'beam-beam-cover-plate-bolted': + data = { + 'beamList': list(Beams.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + } + return Response(data, status=status.HTTP_200_OK) + + # Beam-to-Beam Cover Plate Welded + if slug == 'beam-beam-cover-plate-welded': + data = { + 'beamList': list(Beams.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'weldTypes': [ + {'value': 'Fillet Weld', 'label': 'Fillet Weld'}, + {'value': 'Groove Weld', 'label': 'Groove Weld'} + ], + 'weldFab': [ + {'value': 'Shop Weld', 'label': 'Shop Weld'}, + {'value': 'Field Weld', 'label': 'Field Weld'} + ], + 'thicknessList': thickness_list, + } + return Response(data, status=status.HTTP_200_OK) + + # Beam-Beam End Plate + if slug == 'beam-beam-end-plate': + data = { + 'beamList': list(Beams.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + } + return Response(data, status=status.HTTP_200_OK) + + # Beam-Column End Plate + if slug == 'beam-column-end-plate': + data = { + 'connectivityList': ['Column-Flange-Beam-Web', 'Column Web-Beam Web'], + 'columnList': list(Columns.objects.values_list('Designation', flat=True)), + 'beamList': list(Beams.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + 'boltTypeList': ['Bearing Bolt', 'Friction Grip Bolt'], + } + return Response(data, status=status.HTTP_200_OK) + + # Column-to-Column Cover Plate Bolted + if slug == 'column-column-cover-plate-bolted': + data = { + 'columnList': list(Columns.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + } + return Response(data, status=status.HTTP_200_OK) + + # Column-to-Column Cover Plate Welded + if slug == 'column-column-cover-plate-welded': + data = { + 'columnList': list(Columns.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'weldTypes': [ + {'value': 'Fillet Weld', 'label': 'Fillet Weld'}, + {'value': 'Groove Weld', 'label': 'Groove Weld'} + ], + 'weldFab': [ + {'value': 'Shop Weld', 'label': 'Shop Weld'}, + {'value': 'Field Weld', 'label': 'Field Weld'} + ], + 'thicknessList': thickness_list, + } + return Response(data, status=status.HTTP_200_OK) + + # Column-to-Column End Plate + if slug == 'column-column-end-plate': + data = { + 'columnList': list(Columns.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + 'boltTypeList': ['Bearing Bolt', 'Friction Grip Bolt'], + } + return Response(data, status=status.HTTP_200_OK) + + return Response({'error': f'Sub-module {slug} not found'}, status=404) + except Exception as e: + return Response({'error': str(e)}, status=500) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/cad') + def cad(self, request, submodule_slug=None): + """ + POST /api/modules/moment-connection/{submodule_slug}/cad/ + + Request body: + { + "inputs": {...}, # Design input parameters + "sections": ["Model", "Beam", ...] # Optional: specific sections to generate + } + + Returns: + { + "status": "success", + "files": {section: base64_data, ...}, + "hover_dict": {...}, + "warnings": [...] + } + """ + # Get service from registry + ServiceClass = MomentConnectionRegistry.get_service_by_slug(submodule_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {submodule_slug} not found'}, + status=404 + ) + + # Extract inputs + inputs = request.data.get('inputs', request.data) + + if not inputs: + return Response( + {'error': 'inputs are required'}, + status=400 + ) + + # Get sections from request or use defaults + sections = request.data.get('sections') + if not sections: + sections = get_default_sections('moment-connection', submodule_slug) + + if not sections: + return Response( + {'error': f'No sections defined for {submodule_slug}'}, + status=400 + ) + + try: + # Import adapter to get create_from_input function for hover_dict + create_from_input_func = None + try: + if submodule_slug == 'beam-beam-cover-plate-bolted': + from .submodules.beam_beam_cover_plate_bolted.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'beam-beam-cover-plate-welded': + from .submodules.beam_beam_cover_plate_welded.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'beam-beam-end-plate': + from .submodules.beam_beam_end_plate.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'beam-column-end-plate': + from .submodules.beam_column_end_plate.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'column-column-cover-plate-bolted': + from .submodules.column_column_cover_plate_bolted.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'column-column-cover-plate-welded': + from .submodules.column_column_cover_plate_welded.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'column-column-end-plate': + from .submodules.column_column_end_plate.adapter import create_from_input + create_from_input_func = create_from_input + except ImportError as e: + print(f"[MomentConnectionViewSet] Could not import create_from_input for {submodule_slug}: {e}") + + # Generate CAD models + result = generate_cad_models( + service_class=ServiceClass, + inputs=inputs, + sections=sections, + create_from_input_func=create_from_input_func + ) + + if not result['files']: + return Response( + { + 'status': 'error', + 'message': 'No CAD models were generated', + 'errors': result['warnings'] + }, + status=422 + ) + + return Response({ + 'status': 'success', + 'files': result['files'], + 'hover_dict': result['hover_dict'], + 'message': 'CAD models generated successfully', + 'warnings': result['warnings'] + }, status=201) + + except Exception as e: + print(f"[MomentConnectionViewSet] Error generating CAD: {e}") + import traceback + traceback.print_exc() + return Response( + {'error': str(e), 'status': 'error'}, + status=500 + ) + diff --git a/backend/apps/modules/shear_connection/__init__.py b/backend/apps/modules/shear_connection/__init__.py new file mode 100644 index 000000000..0c0afcf6a --- /dev/null +++ b/backend/apps/modules/shear_connection/__init__.py @@ -0,0 +1,2 @@ +# Shear Connection module package + diff --git a/backend/apps/modules/shear_connection/apps.py b/backend/apps/modules/shear_connection/apps.py new file mode 100644 index 000000000..3bf71d9c6 --- /dev/null +++ b/backend/apps/modules/shear_connection/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class ShearConnectionConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.modules.shear_connection' + diff --git a/backend/apps/modules/shear_connection/registry.py b/backend/apps/modules/shear_connection/registry.py new file mode 100644 index 000000000..3f394bea5 --- /dev/null +++ b/backend/apps/modules/shear_connection/registry.py @@ -0,0 +1,18 @@ +""" +Shear Connection Registry - Auto-discovers sub-modules +Inherits from BaseModuleRegistry (DRY principle) +""" +import os +from apps.core.registry import BaseModuleRegistry + + +class ShearConnectionRegistry(BaseModuleRegistry): + """Registry for shear connection sub-modules""" + pass + + +# Auto-discover sub-modules +_package_name = 'apps.modules.shear_connection.submodules' +_package_path = os.path.join(os.path.dirname(__file__), 'submodules') +ShearConnectionRegistry.auto_discover(_package_name, _package_path) + diff --git a/backend/apps/modules/shear_connection/shared.py b/backend/apps/modules/shear_connection/shared.py new file mode 100644 index 000000000..66699b079 --- /dev/null +++ b/backend/apps/modules/shear_connection/shared.py @@ -0,0 +1,25 @@ +""" +Shared utilities for shear connection modules +""" +from osdag_core.cad.common_logic import CommonDesignLogic +from OCC.Display.backend import * +from osdag_core.Common import * + + +def setup_for_cad(cdl: CommonDesignLogic, module_class): + """Sets up the CommonLogicObjct before generating CAD""" + print("****") + cdl.module_class = module_class # Set the module class in design logic object. + cdl.module_object = module_class # Set the module object (required by common_logic.py) + print("******") + module_object = module_class + print("********") + cdl.loc = module_object.connectivity # Set the connectivity of the module in the design logic object. + if cdl.loc == CONN_CWBW: # If connection type is 'Column Web-Beam Web'. + cdl.connectivityObj = cdl.create3DColWebBeamWeb() # IDK what this does, I guess it creates the connection object. + elif cdl.loc == CONN_CFBW: # If connection type is 'Column Flange-Beam Web'. + cdl.connectivityObj = cdl.create3DColFlangeBeamWeb() # IDK what this does, I guess it creates the connection object. + else: # If it is none of them, + cdl.connectivityObj = cdl.create3DBeamWebBeamWeb() # I guess it creates the last type of connection. + + diff --git a/backend/apps/modules/shear_connection/submodules/__init__.py b/backend/apps/modules/shear_connection/submodules/__init__.py new file mode 100644 index 000000000..19f2128d4 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/__init__.py @@ -0,0 +1,2 @@ +# Sub-modules package + diff --git a/backend/apps/modules/shear_connection/submodules/cleat_angle/__init__.py b/backend/apps/modules/shear_connection/submodules/cleat_angle/__init__.py new file mode 100644 index 000000000..a90d564fa --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/cleat_angle/__init__.py @@ -0,0 +1,5 @@ +""" +Cleat Angle Connection Sub-module +""" +MODULE_ID = 'CleatAngleConnection' +from .service import CleatAngleService as Service diff --git a/backend/apps/modules/shear_connection/submodules/cleat_angle/adapter.py b/backend/apps/modules/shear_connection/submodules/cleat_angle/adapter.py new file mode 100644 index 000000000..7cbc195eb --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/cleat_angle/adapter.py @@ -0,0 +1,581 @@ +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type +) +from apps.modules.shear_connection import shared as scc +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.Common import KEY_CONN +# Will log a lot of unnessecary data. +from osdag_core.design_type.connection.cleat_angle_connection import CleatAngleConnection +from osdag_core.custom_logger import CustomLogger +import sys +import os +from typing import Dict, Any, List +import traceback +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + +def get_required_keys_cleat_angle() -> List[str]: + return [ + "Bolt.Bolt_Hole_Type", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Slip_Factor", + "Bolt.TensionType", + "Bolt.Type", + "Connectivity", + "Connector.Material", + "Design.Design_Method", + "Detailing.Corrosive_Influences", + "Detailing.Edge_type", + "Detailing.Gap", + "Load.Shear", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab", + "Weld.Material_Grade_OverWrite", + "Connector.Angle_List", + ] + +def validate_input(input_values: Dict[str,Any])-> None: + #check iif all required keys exist + required_keys = get_required_keys_cleat_angle() + # check if input_values contains all required keys + missing_keys = contains_keys(input_values, required_keys) + if missing_keys != None: #if keys are missing. + #Raise error for the first missinf key. + raise MissingKeyError(missing_keys[0]) + + #check if Cleat.Angle_Type is a string. + if not isinstance(input_values["Bolt.Bolt_Hole_Type"],str): + #if not raise an error + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type") + + #validate Bolt Diameter + bolt_diameter =input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter,list) + or not validate_list_type(bolt_diameter,str) + or not custom_list_validation(bolt_diameter, int_able)): + raise InvalidInputTypeError( + "Bolt.Diameter","non empty List[str] where all items can be converted to int" + ) + + # validate cleat grade + bolt_grade = input_values["Bolt.Grade"] + if(not isinstance(bolt_grade,list) + or not validate_list_type(bolt_grade,str) + or not custom_list_validation(bolt_grade,float_able)): + + #if any condition fail raise an error + raise InvalidInputTypeError( + "Bolt.Grade", "non empty List[str] where all items can be converted to float" + ) + + #Validate Cleat.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor,str) + or not float_able(bolt_slipfactor)): + raise InvalidInputTypeError( + "Bolt.Slip_Factor", "str where str can be converted to float" + ) + + #Validate Cleat.TensionType + if not isinstance(input_values["Bolt.TensionType"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.TensionType", "str") + + #Validate Cleat.Type + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") # If not, raise error. + + # validate connectivity + if not isinstance(input_values["Connectivity"], str): + # If not, raise error. + raise InvalidInputTypeError("Connectivity", "str") + + #Validate Connector Material + if not isinstance(input_values["Connector.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + # Check if Design.Design_Method is a string. + if not isinstance(input_values["Design.Design_Method"], str): + # If not, raise error. + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + # Check if Detailing.Corrosive_Influences is 'Yes' or 'No'. + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + # If not, raise error. + raise InvalidInputTypeError( + "Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + # Check if Detailing.Edge_type is a string. + if not isinstance(input_values["Detailing.Edge_type"], str): + # If not, raise error. + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) # Check if Detailing.Gap is a string. + or not int_able(detailing_gap)): # Check if Detailing.Gap can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Detailing.Gap", "str where str can be converted to int") + + + # Validate Load.Shear + load_shear = input_values["Load.Shear"] + if (not isinstance(load_shear, str) # Check if Load.Shear is a string. + or not int_able(load_shear)): # Check if Load.Shear can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Shear", "str where str can be converted to int") + + # Validate Material + # Check if Material is a string. + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") # If not, raise error. + + # Validate Member.Supported_Section.Designation + # Check if Member.Supported_Section.Designation is a string. + if not isinstance(input_values["Member.Supported_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supported_Section.Designation", "str") + + # Validate Member.Supported_Section.Material + # Check if Member.Supported_Section.Material is a string. + if not isinstance(input_values["Member.Supported_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Member.Supported_Section.Material", "str") + + # Validate Member.Supporting_Section.Designation + # Check if Member.Supporting_Section.Designation is a string. + if not isinstance(input_values["Member.Supporting_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Designation", "str") + + # Validate Member.Supporting_Section.Material + # Check if Member.Supporting_Section.Material is a string. + if not isinstance(input_values["Member.Supporting_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Material", "str") + + # Validate Module + # Check if Module is a string. + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") # If not, raise error. + + # Validate Weld.Fab + # Check if Weld.Fab is a string. + if not isinstance(input_values["Weld.Fab"], str): + raise InvalidInputTypeError("Weld.Fab", "str") # If not, raise error. + + # Validate Weld.Material_Grade_OverWrite + weld_materialgradeoverwrite = input_values["Weld.Material_Grade_OverWrite"] + if (not isinstance(weld_materialgradeoverwrite, str) # Check if Weld.Material_Grade_OverwWite is a string. + or not int_able(weld_materialgradeoverwrite)): # Check if Weld.Material_Grade_OverWrite can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Weld.Material_Grade_OverWrite", "str where str can be converted to int.") + + # Validate Connector.Plate.Thickness_List + # connector_plate_thicknesslist = input_values["Connector.Angle_List"] + # if (not isinstance(connector_plate_thicknesslist, list) # Check if Connector.Plate.Thickness_List is a list. + # # Check if all items in Connector.Plate.Thickness_List are str. + # or not validate_list_type(connector_plate_thicknesslist, str) + # or not custom_list_validation(connector_plate_thicknesslist, int_able)): # Check if all items in Connector.Plate.Thickness_List can be converted to int. + # raise InvalidInputTypeError( + # "Connector.Angle_List", "List[str] where all items can be converted to int") + + +def validate_input_new(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys_cleat_angle() + print('required_keys : ' , required_keys) + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + print('missing keys : ' , missing_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + print("missing keys is not None") + raise MissingKeyError(missing_keys[0]) + + + # Validate key types using loops. + + # Validate all strings. + str_keys = ["Bolt.Bolt_Hole_Type", # List of all parameters that are strings + "Bolt.TensionType", + "Bolt.Type", + "Bolt.Connectivity", + "Bolt.Connector_Material", + "Design.Design_Method", + "Detailing.Edge_type", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab"] + for key in str_keys: # Loop through all keys. + print('validating string key') + + try : + validate_string(key) # Check if key is a string. If not, raise error. + except : + print('error in validating string keys') + print('string key passed : ' , key ) + + # Validate for keys that are numbers + num_keys = [("Bolt.Slip_Factor", True), # List of all parameters that are numbers (key, is_float) + ("Detailing.Gap", False), + ("Load.Shear", False), + ("Weld.Material_Grade_OverWrite", False)] + for key in num_keys: # Loop through all keys. + # Check if key is a number. If not, raise error. + print('validating num keys') + validate_num(key[0], key[1]) + + # Validate for keys that are arrays + arr_keys = [("Bolt.Diameter", False), # List of all parameters that can be converted to numbers (key, is_float) + ("Bolt.Grade", True)] + # ("Connector.Plate.Thickness_List", False)] + for key in arr_keys: + print('validating arr key') + # Check if key is a list where all items can be converted to numbers. If not, raise error. + validate_arr(key[0], key[1]) + +def create_module() -> CleatAngleConnection: + """Create an instance of the cleat angle connection module design class and set it up for use""" + module = CleatAngleConnection() # Create an instance of the CleatAngleConnection + # Initialize logger to avoid None during member capacity checks + try: + module.set_osdaglogger(None, id="web") + except Exception as log_e: + print("Warning: failed to init logger:", log_e) + return module + +def create_from_input(input_values: Dict[str, Any]) -> CleatAngleConnection: + """Create an instance of the cleat angle connection module design class from input values.""" + # validate_input(input_values) + print('CleatAngle - create_from_input called with input_values:', input_values) + + try : + module = create_module() # Create module instance. + print('CleatAngle - create_module successful, module:', module) + except Exception as e : + print('e in create_module : ' , e) + print('error in creating module') + traceback.print_exc() + return None + + # Map frontend keys to osdag_core keys + # Frontend sends 'Connectivity' but osdag_core expects 'Connectivity *' (KEY_CONN) + design_dictionary = input_values.copy() + if 'Connectivity' in design_dictionary and KEY_CONN not in design_dictionary: + design_dictionary[KEY_CONN] = design_dictionary.pop('Connectivity') + + # Set the input values on the module instance. + print('CleatAngle - About to call module.set_input_values') + print('CleatAngle - Section designations in input:') + print(' - Supporting Section (Column):', design_dictionary.get('Member.Supporting_Section.Designation')) + print(' - Supported Section (Beam):', design_dictionary.get('Member.Supported_Section.Designation')) + print(' - Connectivity:', design_dictionary.get(KEY_CONN)) + + try : + module.set_input_values(design_dictionary) + print('CleatAngle - module.set_input_values successful') + except Exception as e : + + traceback.print_exc() + print('e in set_input_values : ' , e) + print('error in setting the input values') + print('CleatAngle - Exception type:', type(e)) + print('CleatAngle - Exception args:', e.args) + + return module + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """ + Generate, format and return the input values from the given output values. + Output format (json): { + "Bolt.Pitch": + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)" + "value": 40 + } + } + """ + print('CleatAngle - generate_output called with input_values:', input_values) + + output = {} # Dictionary for formatted values + module = create_from_input(input_values) # Create module from input. + print('module : ' , module) + print('type of module : ' , type(module)) + + if module is None: + print('CleatAngle - Module creation failed, returning empty output') + return {}, [] + + # Check if module has required attributes + if not hasattr(module, 'output_values'): + print('CleatAngle - Module does not have output_values method') + return {}, [] + + print('CleatAngle - About to call module output methods') + + try: + # Check if module has required attributes for output generation + required_attrs = ['cleat', 'bolt', 'sptd_leg', 'spting_leg'] + missing_attrs = [] + for attr in required_attrs: + if not hasattr(module, attr): + missing_attrs.append(attr) + + if missing_attrs: + print(f'CleatAngle - Module missing required attributes: {missing_attrs}') + print('CleatAngle - This indicates set_input_values failed. Module not properly initialized.') + return {}, [] + + # Generate output values in unformatted form. + raw_output_text = module.output_values(True) + print('CleatAngle - raw_output_text:', raw_output_text) + print(f'CleatAngle - raw_output_text length: {len(raw_output_text)}') + except Exception as e: + print('CleatAngle - Error calling output_values:', e) + traceback.print_exc() + raw_output_text = [] + + try: + raw_output_spacing_supported = module.spacing(True) # Generate output val (supported side) + print('CleatAngle - raw_output_spacing_supported:', raw_output_spacing_supported) + except Exception as e: + print('CleatAngle - Error calling spacing (supported):', e) + raw_output_spacing_supported = [] + + try: + raw_output_spacing_supporting = module.spting_spacing(True) # supporting side spacing + print('CleatAngle - raw_output_spacing_supporting:', raw_output_spacing_supporting) + except Exception as e: + print('CleatAngle - Error calling spting_spacing (supporting):', e) + raw_output_spacing_supporting = [] + + try: + # raw_output_capacities = module.capacities(True) + raw_bolt_capacity_supported = module.bolt_capacity_details_supported(True) + print('CleatAngle - raw_bolt_capacity_supported:', raw_bolt_capacity_supported) + except Exception as e: + print('CleatAngle - Error calling bolt_capacity_details_supported:', e) + raw_bolt_capacity_supported = [] + + try: + raw_bolt_capacity_suporting = module.bolt_capacity_details_suporting(True) + print('CleatAngle - raw_bolt_capacity_suporting:', raw_bolt_capacity_suporting) + except Exception as e: + print('CleatAngle - Error calling bolt_capacity_details_suporting:', e) + raw_bolt_capacity_suporting = [] + + # Add suffixes to duplicate-prone supported keys + raw_supported = [ + (f"{key}_supported", label, typ, value, visible) + for key, label, typ, value, visible in raw_bolt_capacity_supported + if key # only if key is not None + ] + + raw_supporting = [ + (f"{key}_supporting", label, typ, value, visible) + for key, label, typ, value, visible in raw_bolt_capacity_suporting + if key + ] + + # Create spacing outputs with side-specific suffixes to avoid key collisions + raw_spacing_supported = [ + (f"{key}_supported", label, typ, value, visible if len(item) == 5 else True) + for item in raw_output_spacing_supported + if len(item) >= 4 and item[0] + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_spacing_supporting = [ + (f"{key}_supporting", label, typ, value, visible if len(item) == 5 else True) + for item in raw_output_spacing_supporting + if len(item) >= 4 and item[0] + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + # Prefer CustomLogger if attached + if hasattr(module, 'logger') and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() + print(f'CleatAngle - Retrieved {len(logs)} logs from CustomLogger') + else: + logs = module.logs if hasattr(module, 'logs') else [] + print(f'CleatAngle - Module logs: {logs}') + # Ensure logs is a list if empty + if not logs: + logs = ["No logs generated"] + print("CleatAngle - Setting default logs message") + + raw_output = raw_spacing_supported + raw_spacing_supporting + raw_output_text + raw_supported + raw_supporting + print(f'CleatAngle - Total raw_output items: {len(raw_output)}') + print(f'CleatAngle - Raw output sample: {raw_output[:5] if raw_output else "Empty"}') + + # os.system("clear") + # Loop over all the text values and add them to ouptut dict. + for param in raw_output: + if param[2] == "TextBox": # If the parameter is a text output, + key = param[0] # id/key + label = param[1] # label text. + value = param[3] # Value as string. + print(f'CleatAngle - Adding to output: {key} = {value}') + output[key] = { + "key": key, + "label": label, + "val": value # Changed from "value" to "val" to match frontend expectations + } # Set label, key and value in output + + print(f'CleatAngle - Final output keys: {list(output.keys())}') + print(f'CleatAngle - Final output size: {len(output)}') + print(f'CleatAngle - Final logs being returned: {logs}') + return output, logs + +#we do not have plate in just like in finplate case, we have cleatAngle which is combination of angle & nutbolts +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + from apps.core.utils import write_stl + from OCC.Core.BRep import BRep_Builder + from OCC.Core.TopoDS import TopoDS_Compound + from OCC.Core.Message import Message_ProgressRange + + if section not in ("Model", "Beam", "Column", "cleatAngle"): # Error checking: If section is valid. + raise InvalidInputTypeError( + "section", "'Model', 'Beam', 'Column' or 'cleatAngle'") + + # First check if we have valid output before attempting CAD generation + try: + output, logs = generate_output(input_values) + if not output or len(output) == 0: + print('CleatAngle CAD - No valid output found. Cannot generate CAD model.') + raise ValueError("Cannot generate CAD model: No valid design output found. Please ensure the design calculation completed successfully.") + except Exception as e: + print(f'CleatAngle CAD - Error checking output: {e}') + raise ValueError(f"Cannot generate CAD model: Design calculation failed - {str(e)}") + + module = create_from_input(input_values) # Create module from input. + print('module from input values : ' , module) + # Object that will create the CAD model. + try : + # CommonDesignLogic(display, cad_widget, folder, connection, mainmodule) + from osdag_core.Common import KEY_DISP_CLEATANGLE + cld = CommonDesignLogic(None, None, '', KEY_DISP_CLEATANGLE, module.mainmodule) + except Exception as e : + print('error in cld e : ' , e) + + try : + # Setup the calculations object for generating CAD model. + scc.setup_for_cad(cld, module) + except Exception as e : + import traceback + traceback.print_exc() + print('Error in setting up cad e : ' , e) + + cld.component = section + + part_names = ["Beam", "Column", "cleatAngle", "Weld", "Welds", "Bolt", "Bolts"] + try: + if section == "Model": + # Build compound by adding each part shape without fusing + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + for part in part_names: + try: + cld.component = part + part_shape = cld.create2Dcad() + if part_shape is None: + continue + builder.Add(compound, part_shape) + part_file_name = f"{session}_{part}.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + full_brep_path = os.path.join(os.getcwd(), part_file_path_rel) + from OCC.Core import BRepTools + BRepTools.breptools.Write(part_shape, full_brep_path, Message_ProgressRange()) + print(f"[CleatAngle CAD] Wrote BREP for {part} at {full_brep_path}") + # STL as well + part_stl_file = part_file_path_rel.replace(".brep", ".stl") + try: + write_stl(part_shape, os.path.join(os.getcwd(), part_stl_file)) + print(f"[CleatAngle CAD] Wrote STL for {part} at {os.path.join(os.getcwd(), part_stl_file)}") + except Exception as e: + print(f"Failed to write STL for part {part} (CleatAngle):", e) + except Exception as e: + print(f"Failed generating cad part {part} in CleatAngle: {e}") + + # Now write the compound as the Model + cld.component = "Model" + model = compound + compound_file_name = f"{session}_Model.brep" + compound_file_path_rel = os.path.join("file_storage", "cad_models", compound_file_name) + from OCC.Core import BRepTools + full_compound_path = os.path.join(os.getcwd(), compound_file_path_rel) + BRepTools.breptools.Write(model, full_compound_path, Message_ProgressRange()) + print(f"[CleatAngle CAD] Wrote Model BREP at {full_compound_path}") + # Compound/model STL (for completeness, not loaded in UI) + compound_stl_file = compound_file_path_rel.replace(".brep", ".stl") + try: + write_stl(model, os.path.join(os.getcwd(), compound_stl_file)) + print(f"[CleatAngle CAD] Wrote Model STL at {os.path.join(os.getcwd(), compound_stl_file)}") + except Exception as e: + print("Failed to write Model STL for CleatAngle:", e) + return compound_file_path_rel + else: + try : + model = cld.create2Dcad() # Generate CAD Model. + except Exception as e : + print('Error in cld.create2Dcad() e : ' , e) + return False + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + print('2d model : ' , model) + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + print('brep file path in create_cad_model : ' , file_path) + try : + from OCC.Core import BRepTools + full_brep = os.path.join(os.getcwd(), file_path) + BRepTools.breptools.Write(model, full_brep, Message_ProgressRange()) + print(f"[CleatAngle CAD] Wrote BREP for {section} at {full_brep}") + # Write STL too + stl_file_path = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_file_path) + write_stl(model, full_stl) + print(f"[CleatAngle CAD] Wrote STL for {section} at {full_stl}") + except Exception as e : + print('Writing to BREP or STL file failed e : ' , e) + return file_path + except Exception as top_e: + print('Top-level error in CleatAngle create_cad_model:', top_e) + return False + + diff --git a/backend/apps/modules/shear_connection/submodules/cleat_angle/service.py b/backend/apps/modules/shear_connection/submodules/cleat_angle/service.py new file mode 100644 index 000000000..34caab745 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/cleat_angle/service.py @@ -0,0 +1,26 @@ +""" +Cleat Angle Service - Business logic layer +""" +from osdag_core.design_type.connection.cleat_angle_connection import CleatAngleConnection +from .adapter import validate_input, generate_output, create_cad_model + + +class CleatAngleService: + """Service class for Cleat Angle Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + validate_input(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs, + 'success': True + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/shear_connection/submodules/end_plate/__init__.py b/backend/apps/modules/shear_connection/submodules/end_plate/__init__.py new file mode 100644 index 000000000..897113540 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/end_plate/__init__.py @@ -0,0 +1,5 @@ +""" +End Plate Connection Sub-module +""" +MODULE_ID = 'EndPlateConnection' +from .service import EndPlateService as Service diff --git a/backend/apps/modules/shear_connection/submodules/end_plate/adapter.py b/backend/apps/modules/shear_connection/submodules/end_plate/adapter.py new file mode 100644 index 000000000..26ed00af7 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/end_plate/adapter.py @@ -0,0 +1,527 @@ +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type +) +from apps.modules.shear_connection import shared as scc +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.Common import KEY_DISP_ENDPLATE, KEY_CONN +from osdag_core.custom_logger import CustomLogger +# Will log a lot of unnessecary data. +from osdag_core.design_type.connection.fin_plate_connection import FinPlateConnection +from osdag_core.design_type.connection.end_plate_connection import EndPlateConnection +import sys +import os +import typing +from typing import Dict, Any, List +import traceback +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + return [ + "Bolt.Bolt_Hole_Type", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Slip_Factor", + "Bolt.TensionType", + "Bolt.Type", + "Connectivity", + "Connector.Material", + "Design.Design_Method", + "Detailing.Corrosive_Influences", + "Detailing.Edge_type", + "Detailing.Gap", + "Load.Axial", + "Load.Shear", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab", + "Weld.Material_Grade_OverWrite", + "Connector.Plate.Thickness_List", + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key.a + raise MissingKeyError(missing_keys[0]) + + # Validate key types one by one: + + # Validate Bolt.Bolt_Hole_Type. + # Check if Bolt.Bolt_Hole_Type is a string. + if not isinstance(input_values["Bolt.Bolt_Hole_Type"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type", "str") + + # Validate Bolt.Diameter. + bolt_diameter = input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter, list) # Check if Bolt.Diameter is a list. + # Check if all items in Bolt.Diameter are str. + or not validate_list_type(bolt_diameter, str) + or not custom_list_validation(bolt_diameter, int_able)): # Check if all items in Bolt.Diameter can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Diameter", "non empty List[str] where all items can be converted to int") + + # Validate Bolt.Grade + bolt_grade = input_values["Bolt.Grade"] + if (not isinstance(bolt_grade, list) # Check if Bolt.Grade is a list. + # Check if all items in Bolt.Grade are str. + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): # Check if all items in Bolt.Grade can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Grade", "non empty List[str] where all items can be converted to float") + + # Validate Bolt.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor, str) # Check if Bolt.Slip_Factor is a string. + or not float_able(bolt_slipfactor)): # Check if Bolt.Slip_Factor can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Slip_Factor", "str where str can be converted to float") + + # Validate Bolt.TensionType + # Check if Bolt.TensionType is a string. + if not isinstance(input_values["Bolt.TensionType"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.TensionType", "str") + + # Validate Bolt.Type + # Check if Bolt.Type is a string. + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") # If not, raise error. + + # Validate Connectivity + # Check if Connectivity is a string. + if not isinstance(input_values["Connectivity"], str): + # If not, raise error. + raise InvalidInputTypeError("Connectivity", "str") + + # Validate Connector.Material + # Check if Connector.Material is a string. + if not isinstance(input_values["Connector.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + # Check if Design.Design_Method is a string. + if not isinstance(input_values["Design.Design_Method"], str): + # If not, raise error. + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + # Check if Detailing.Corrosive_Influences is 'Yes' or 'No'. + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + # If not, raise error. + raise InvalidInputTypeError( + "Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + # Check if Detailing.Edge_type is a string. + if not isinstance(input_values["Detailing.Edge_type"], str): + # If not, raise error. + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) # Check if Detailing.Gap is a string. + or not int_able(detailing_gap)): # Check if Detailing.Gap can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) # Check if Load.Axial is a string. + or not int_able(load_axial)): # Check if Load.Axial can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Axial", "str where str can be converted to int") + + # Validate Load.Shear + load_shear = input_values["Load.Shear"] + if (not isinstance(load_shear, str) # Check if Load.Shear is a string. + or not int_able(load_shear)): # Check if Load.Shear can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Shear", "str where str can be converted to int") + + # Validate Material + # Check if Material is a string. + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") # If not, raise error. + + # Validate Member.Supported_Section.Designation + # Check if Member.Supported_Section.Designation is a string. + if not isinstance(input_values["Member.Supported_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supported_Section.Designation", "str") + + # Validate Member.Supported_Section.Material + # Check if Member.Supported_Section.Material is a string. + if not isinstance(input_values["Member.Supported_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Member.Supported_Section.Material", "str") + + # Validate Member.Supporting_Section.Designation + # Check if Member.Supporting_Section.Designation is a string. + if not isinstance(input_values["Member.Supporting_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Designation", "str") + + # Validate Member.Supporting_Section.Material + # Check if Member.Supporting_Section.Material is a string. + if not isinstance(input_values["Member.Supporting_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Material", "str") + + # Validate Module + # Check if Module is a string. + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") # If not, raise error. + + # Validate Weld.Fab + # Check if Weld.Fab is a string. + if not isinstance(input_values["Weld.Fab"], str): + raise InvalidInputTypeError("Weld.Fab", "str") # If not, raise error. + + # Validate Weld.Material_Grade_OverWrite + weld_materialgradeoverwrite = input_values["Weld.Material_Grade_OverWrite"] + if (not isinstance(weld_materialgradeoverwrite, str) # Check if Weld.Material_Grade_OverwWite is a string. + or not int_able(weld_materialgradeoverwrite)): # Check if Weld.Material_Grade_OverWrite can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Weld.Material_Grade_OverWrite", "str where str can be converted to int.") + + # Validate Connector.Plate.Thickness_List + connector_plate_thicknesslist = input_values["Connector.Plate.Thickness_List"] + if (not isinstance(connector_plate_thicknesslist, list) # Check if Connector.Plate.Thickness_List is a list. + # Check if all items in Connector.Plate.Thickness_List are str. + or not validate_list_type(connector_plate_thicknesslist, str) + or not custom_list_validation(connector_plate_thicknesslist, int_able)): # Check if all items in Connector.Plate.Thickness_List can be converted to int. + raise InvalidInputTypeError( + "Connector.Plate.Thickness_List", "List[str] where all items can be converted to int") + + +def validate_input_new(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + print('required_keys : ' , required_keys) + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + print('missing keys : ' , missing_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + print("missing keys is not None") + raise MissingKeyError(missing_keys[0]) + + # Validate key types using loops. + + # Validate all strings. + str_keys = ["Bolt.Bolt_Hole_Type", # List of all parameters that are strings + "Bolt.TensionType", + "Bolt.Type", + "Bolt.Connectivity", + "Bolt.Connector_Material", + "Design.Design_Method", + "Detailing.Edge_type", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab"] + for key in str_keys: # Loop through all keys. + print('validating string key') + + try : + validate_string(key) # Check if key is a string. If not, raise error. + except : + print('error in validating string keys') + print('string key passed : ' , key ) + + # Validate for keys that are numbers + num_keys = [("Bolt.Slip_Factor", True), # List of all parameters that are numbers (key, is_float) + ("Detailing.Gap", False), + ("Load.Axial", False), + ("Load.Shear", False), + ("Weld.Material_Grade_OverWrite", False)] + for key in num_keys: # Loop through all keys. + # Check if key is a number. If not, raise error. + print('validating num keys') + validate_num(key[0], key[1]) + + # Validate for keys that are arrays + arr_keys = [("Bolt.Diameter", False), # List of all parameters that can be converted to numbers (key, is_float) + ("Bolt.Grade", True), + ("Connector.Plate.Thickness_List", False)] + for key in arr_keys: + print('validating arr key') + # Check if key is a list where all items can be converted to numbers. If not, raise error. + validate_arr(key[0], key[1]) + + +def create_module() -> EndPlateConnection: + """Create an instance of the End plate connection module design class and set it up for use""" + module = EndPlateConnection() # Create an instance of the EndPlateConnection + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> EndPlateConnection: + """Create an instance of the End plate connection module design class from input values.""" + # validate_input(input_values) + print("\n[EndPlateAdapter.create_from_input] called") + print(f"[EndPlateAdapter.create_from_input] input keys={list(input_values.keys()) if isinstance(input_values, dict) else type(input_values)}") + try: + module = create_module() # Create module instance. + print(f"[EndPlateAdapter.create_from_input] module created={type(module).__name__}") + except Exception as e: + print('e in create_module : ' , e) + print('error in creating module') + traceback.print_exc() + raise + + # Map frontend keys to osdag_core keys + # Frontend sends 'Connectivity' but osdag_core expects 'Connectivity *' (KEY_CONN) + design_dictionary = input_values.copy() + if 'Connectivity' in design_dictionary and KEY_CONN not in design_dictionary: + design_dictionary[KEY_CONN] = design_dictionary.pop('Connectivity') + print(f"[EndPlateAdapter.create_from_input] mapped 'Connectivity' -> {KEY_CONN!r} value={design_dictionary.get(KEY_CONN)!r}") + else: + print(f"[EndPlateAdapter.create_from_input] connectivity mapping skipped; has KEY_CONN={KEY_CONN in design_dictionary}") + + # Set the input values on the module instance. + try: + print(f"[EndPlateAdapter.create_from_input] set_input_values with keys={list(design_dictionary.keys())[:20]}") + module.set_input_values(design_dictionary) + print("[EndPlateAdapter.create_from_input] set_input_values passed") + except Exception as e: + print('e in set_input_values : ' , e) + print('error in setting the input values') + traceback.print_exc() + raise + + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """ + Generate, format and return the input values from the given output values. + Output format (json): { + "Bolt.Pitch": + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)" + "value": 40 + } + } + """ + print("\n[EndPlateAdapter.generate_output] called") + output = {} # Dictionary for formatted values + module = create_from_input(input_values) # Create module from input. + if module is None: + raise RuntimeError("create_from_input returned None module") + + print(f"[EndPlateAdapter.generate_output] module={type(module).__name__}") + print(f"[EndPlateAdapter.generate_output] has logger={hasattr(module, 'logger')}") + # Generate output values in unformatted form. + try: + raw_output_text = module.output_values(True) + print(f"[EndPlateAdapter.generate_output] raw_output_text={len(raw_output_text)}") + raw_output_spacing = module.spacing(True) # Generate output val + print(f"[EndPlateAdapter.generate_output] raw_output_spacing={len(raw_output_spacing)}") + raw_output_capacities = module.capacities(True) + print(f"[EndPlateAdapter.generate_output] raw_output_capacities={len(raw_output_capacities)}") + raw_output_bolt_capacity = module.bolt_capacity_details(True) + print(f"[EndPlateAdapter.generate_output] raw_output_bolt_capacity={len(raw_output_bolt_capacity)}") + except Exception as e: + print(f"[EndPlateAdapter.generate_output] error while collecting raw outputs: {type(e).__name__}: {e}") + traceback.print_exc() + raise + + # Prefer CustomLogger if attached + if hasattr(module, 'logger') and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() + else: + logs = module.logs if hasattr(module, 'logs') else [] + # Ensure logs is a list even if empty + if not logs: + logs = ["No logs generated"] + + raw_output = raw_output_capacities + raw_output_spacing + raw_output_text + raw_output_bolt_capacity + print(f"[EndPlateAdapter.generate_output] total_raw_output={len(raw_output)}") + # os.system("clear") + # Loop over all the text values and add them to ouptut dict. + for idx, param in enumerate(raw_output): + if not isinstance(param, (tuple, list)): + print(f"[EndPlateAdapter.generate_output] skipping non-sequence param at idx={idx}: {type(param)}") + continue + if len(param) < 4: + print(f"[EndPlateAdapter.generate_output] skipping short param at idx={idx}: {param}") + continue + if param[2] == "TextBox": # If the parameter is a text output, + key = param[0] # id/key + label = param[1] # label text. + value = param[3] # Value as string. + output[key] = { + "key": key, + "label": label, + "val": value # Changed from "value" to "val" to match frontend expectations + } # Set label, key and value in output + print(f"[EndPlateAdapter.generate_output] output keys count={len(output)}") + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + from apps.core.utils import write_stl + from OCC.Core.BRep import BRep_Builder + from OCC.Core.TopoDS import TopoDS_Compound + from OCC.Core.Message import Message_ProgressRange + + if section not in ("Model", "Beam", "Column", "Plate"): # Error checking: If section is valid. + raise InvalidInputTypeError( + "section", "'Model', 'Beam', 'Column' or 'Plate'") + module = create_from_input(input_values) # Create module from input. + # Object that will create the CAD model. + try : + cld = CommonDesignLogic(None, None, '', KEY_DISP_ENDPLATE, module.mainmodule) + except Exception as e : + print('error in cld e : ' , e) + + try : + # Setup the calculations object for generating CAD model. + scc.setup_for_cad(cld, module) + except Exception as e : + print('Error in setting up cad e : ' , e) + + # The section of the module that will be generated. + cld.component = section + + part_names = ["Beam", "Column", "Plate", "Weld", "Welds", "Bolt", "Bolts"] + part_files = {} + compound_model = None + + try: + if section == "Model": + # Build compound by adding each part shape without fusing + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + + for part in part_names: + try: + # Generate shape for this part + cld.component = part + part_shape = cld.create2Dcad() + if part_shape is None: + continue + # Add to compound + builder.Add(compound, part_shape) + # Ensure per-part BREP file exists (write or overwrite) + part_file_name = f"{session}_{part}.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + full_brep_path = os.path.join(os.getcwd(), part_file_path_rel) + # Write per-part BREP + from OCC.Core import BRepTools + BRepTools.breptools.Write(part_shape, full_brep_path, Message_ProgressRange()) + try: + module.logs.append(f"Wrote {part} BREP: {full_brep_path}") + except Exception: + pass + # Ensure per-part STL as well + part_stl_file = part_file_path_rel.replace(".brep", ".stl") + full_stl_path = os.path.join(os.getcwd(), part_stl_file) + try: + write_stl(part_shape, full_stl_path) + try: + module.logs.append(f"Wrote {part} STL: {full_stl_path}") + except Exception: + pass + except Exception as e: + print(f"Failed to write STL for part {part}:", e) + except Exception as e: + print(f"Failed generating cad part {part} in EndPlate: {e}") + + # Now write the compound as the Model + cld.component = "Model" # Optionally switch context + model = compound + compound_file_name = f"{session}_Model.brep" + compound_file_path_rel = os.path.join("file_storage", "cad_models", compound_file_name) + BRepTools.breptools.Write(model, os.path.join(os.getcwd(), compound_file_path_rel), Message_ProgressRange()) + try: + module.logs.append(f"Wrote Model BREP: {os.path.join(os.getcwd(), compound_file_path_rel)}") + except Exception: + pass + # Compound/model STL (for completeness, but will not be loaded in UI) + compound_stl_file = compound_file_path_rel.replace(".brep", ".stl") + try: + write_stl(model, os.path.join(os.getcwd(), compound_stl_file)) + try: + module.logs.append(f"Wrote Model STL: {os.path.join(os.getcwd(), compound_stl_file)}") + except Exception: + pass + except Exception as e: + print("Failed to write Model STL for EndPlate:", e) + return compound_file_path_rel + else: + try : + model = cld.create2Dcad() # Generate CAD Model. + except Exception as e : + print('Error in cld.create2Dcad() e : ' , e) + return False + # check if the cad_models folder exists or not + # if no, then create one + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + try : + from OCC.Core import BRepTools + full_brep = os.path.join(os.getcwd(), file_path) + BRepTools.breptools.Write(model, full_brep, Message_ProgressRange()) # Generate CAD Model + try: + module.logs.append(f"Wrote {section} BREP: {full_brep}") + except Exception: + pass + # Write STL too + stl_file_path = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_file_path) + write_stl(model, full_stl) + try: + module.logs.append(f"Wrote {section} STL: {full_stl}") + except Exception: + pass + except Exception as e : + print('Writing to BREP or STL file failed e : ' , e) + return file_path + except Exception as top_e: + print('Top-level error in EndPlate create_cad_model:', top_e) + return False + diff --git a/backend/apps/modules/shear_connection/submodules/end_plate/service.py b/backend/apps/modules/shear_connection/submodules/end_plate/service.py new file mode 100644 index 000000000..896617ee9 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/end_plate/service.py @@ -0,0 +1,52 @@ +""" +End Plate Service - Business logic layer +""" +from osdag_core.design_type.connection.end_plate_connection import EndPlateConnection +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class EndPlateService: + """Service class for End Plate Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + print("\n" + "=" * 80) + print("[EndPlateService.calculate] called") + print(f"[EndPlateService.calculate] input keys={list(inputs.keys()) if isinstance(inputs, dict) else type(inputs)}") + if isinstance(inputs, dict): + for k in [ + "Connectivity", + "Bolt.Type", + "Member.Supported_Section.Designation", + "Member.Supporting_Section.Designation", + "Connector.Plate.Thickness_List", + "Module", + ]: + if k in inputs: + print(f"[EndPlateService.calculate] {k}={inputs.get(k)!r}") + try: + validate_input(inputs) + print("[EndPlateService.calculate] validate_input passed") + output, logs = generate_output(inputs) + print(f"[EndPlateService.calculate] output_count={len(output) if isinstance(output, dict) else 'NA'}") + print(f"[EndPlateService.calculate] logs_count={len(logs) if isinstance(logs, list) else 'NA'}") + print("=" * 80 + "\n") + return { + 'data': output, + 'logs': logs, + 'success': True + } + except Exception as e: + print("[EndPlateService.calculate] ERROR") + print(f"[EndPlateService.calculate] exception={type(e).__name__}: {e}") + traceback.print_exc() + print("=" * 80 + "\n") + raise + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/shear_connection/submodules/fin_plate/__init__.py b/backend/apps/modules/shear_connection/submodules/fin_plate/__init__.py new file mode 100644 index 000000000..293707f38 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/fin_plate/__init__.py @@ -0,0 +1,5 @@ +""" +FinPlateConnection Sub-module +""" +MODULE_ID = 'FinPlateConnection' +from .service import FinPlateService as Service diff --git a/backend/apps/modules/shear_connection/submodules/fin_plate/adapter.py b/backend/apps/modules/shear_connection/submodules/fin_plate/adapter.py new file mode 100644 index 000000000..3778c3504 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/fin_plate/adapter.py @@ -0,0 +1,698 @@ +""" +Api for FinPlateConnection module +Functions: + get_required_keys() -> List[str]: + Return all required input parameters for the module. + validate_input(input_values: Dict[str, Any]) -> None: + Go through all the input parameters. + Check if all required parameters are given. + Check if all parameters are of correct data type. + create_module() -> FinPlateConnection: + Create an instance of the FinPlateConnection module design class and set it up for use + create_from_input(input_values: Dict[str, Any]) -> FinPlateConnection + Create an instance of the FinPlateConnection module design class from input values. + generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + Generate, format and return the input values from the given output values. + Output format (json): { + "Bolt.Pitch": + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)" + "val": 40 + } + } + create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + Generate the CAD model from input values as a BREP file. Return file path. +""" +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type +) +from apps.modules.shear_connection import shared as scc +from OCC.Core import BRepTools +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer + +# from OCC.Core.StlAPI import StlAPI_Writer +# from OCC.Core.TopoDS import TopoDS_Solid, TopoDS_Shell +from osdag_core.cad.common_logic import CommonDesignLogic +# Will log a lot of unnessecary data. +from osdag_core.design_type.connection.fin_plate_connection import FinPlateConnection +from osdag_core.Common import KEY_DISP_FINPLATE, KEY_CONN +from osdag_core.custom_logger import CustomLogger +import sys +import os +import typing +from typing import Dict, Any, List +import traceback +import json +from apps.core.utils import write_stl + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + return [ + "Bolt.Bolt_Hole_Type", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Slip_Factor", + "Bolt.TensionType", + "Bolt.Type", + "Connectivity", + "Connector.Material", + "Design.Design_Method", + "Detailing.Corrosive_Influences", + "Detailing.Edge_type", + "Detailing.Gap", + "Load.Axial", + "Load.Shear", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab", + "Weld.Material_Grade_OverWrite", + "Connector.Plate.Thickness_List", + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + raise MissingKeyError(missing_keys[0]) + + # Validate key types one by one: + + # Validate Bolt.Bolt_Hole_Type. + # Check if Bolt.Bolt_Hole_Type is a string. + if not isinstance(input_values["Bolt.Bolt_Hole_Type"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type", "str") + + # Validate Bolt.Diameter. + bolt_diameter = input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter, list) # Check if Bolt.Diameter is a list. + # Check if all items in Bolt.Diameter are str. + or not validate_list_type(bolt_diameter, str) + or not custom_list_validation(bolt_diameter, int_able)): # Check if all items in Bolt.Diameter can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Diameter", "non empty List[str] where all items can be converted to int") + + # Validate Bolt.Grade + bolt_grade = input_values["Bolt.Grade"] + if (not isinstance(bolt_grade, list) # Check if Bolt.Grade is a list. + # Check if all items in Bolt.Grade are str. + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): # Check if all items in Bolt.Grade can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Grade", "non empty List[str] where all items can be converted to float") + + # Validate Bolt.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor, str) # Check if Bolt.Slip_Factor is a string. + or not float_able(bolt_slipfactor)): # Check if Bolt.Slip_Factor can be converted to float. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Bolt.Slip_Factor", "str where str can be converted to float") + + # Validate Bolt.TensionType + # Check if Bolt.TensionType is a string. + if not isinstance(input_values["Bolt.TensionType"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.TensionType", "str") + + # Validate Bolt.Type + # Check if Bolt.Type is a string. + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") # If not, raise error. + + # Validate Connectivity + # Check if Connectivity is a string. + if not isinstance(input_values["Connectivity"], str): + # If not, raise error. + raise InvalidInputTypeError("Connectivity", "str") + + # Validate Connector.Material + # Check if Connector.Material is a string. + if not isinstance(input_values["Connector.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + # Check if Design.Design_Method is a string. + if not isinstance(input_values["Design.Design_Method"], str): + # If not, raise error. + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + # Check if Detailing.Corrosive_Influences is 'Yes' or 'No'. + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + # If not, raise error. + raise InvalidInputTypeError( + "Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + # Check if Detailing.Edge_type is a string. + if not isinstance(input_values["Detailing.Edge_type"], str): + # If not, raise error. + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) # Check if Detailing.Gap is a string. + or not int_able(detailing_gap)): # Check if Detailing.Gap can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) # Check if Load.Axial is a string. + or not int_able(load_axial)): # Check if Load.Axial can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Axial", "str where str can be converted to int") + + # Validate Load.Shear + load_shear = input_values["Load.Shear"] + if (not isinstance(load_shear, str) # Check if Load.Shear is a string. + or not int_able(load_shear)): # Check if Load.Shear can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Shear", "str where str can be converted to int") + + # Validate Material + # Check if Material is a string. + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") # If not, raise error. + + # Validate Member.Supported_Section.Designation + # Check if Member.Supported_Section.Designation is a string. + if not isinstance(input_values["Member.Supported_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supported_Section.Designation", "str") + + # Validate Member.Supported_Section.Material + # Check if Member.Supported_Section.Material is a string. + if not isinstance(input_values["Member.Supported_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Member.Supported_Section.Material", "str") + + # Validate Member.Supporting_Section.Designation + # Check if Member.Supporting_Section.Designation is a string. + if not isinstance(input_values["Member.Supporting_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Designation", "str") + + # Validate Member.Supporting_Section.Material + # Check if Member.Supporting_Section.Material is a string. + if not isinstance(input_values["Member.Supporting_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Material", "str") + + # Validate Module + # Check if Module is a string. + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") # If not, raise error. + + # Validate Weld.Fab + # Check if Weld.Fab is a string. + if not isinstance(input_values["Weld.Fab"], str): + raise InvalidInputTypeError("Weld.Fab", "str") # If not, raise error. + + # Validate Weld.Material_Grade_OverWrite + weld_materialgradeoverwrite = input_values["Weld.Material_Grade_OverWrite"] + if (not isinstance(weld_materialgradeoverwrite, str) # Check if Weld.Material_Grade_OverwWite is a string. + or not int_able(weld_materialgradeoverwrite)): # Check if Weld.Material_Grade_OverWrite can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Weld.Material_Grade_OverWrite", "str where str can be converted to int.") + + # Validate Connector.Plate.Thickness_List + connector_plate_thicknesslist = input_values["Connector.Plate.Thickness_List"] + if (not isinstance(connector_plate_thicknesslist, list) # Check if Connector.Plate.Thickness_List is a list. + # Check if all items in Connector.Plate.Thickness_List are str. + or not validate_list_type(connector_plate_thicknesslist, str) + or not custom_list_validation(connector_plate_thicknesslist, int_able)): # Check if all items in Connector.Plate.Thickness_List can be converted to int. + raise InvalidInputTypeError( + "Connector.Plate.Thickness_List", "List[str] where all items can be converted to int") + + +def validate_input_new(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys() + print('required_keys : ' , required_keys) + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + print('missing keys : ' , missing_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + print("missing keys is not None") + raise MissingKeyError(missing_keys[0]) + + # Validate key types using loops. + + # Validate all strings. + str_keys = ["Bolt.Bolt_Hole_Type", # List of all parameters that are strings + "Bolt.TensionType", + "Bolt.Type", + "Bolt.Connectivity", + "Bolt.Connector_Material", + "Design.Design_Method", + "Detailing.Edge_type", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab"] + for key in str_keys: # Loop through all keys. + print('validating string key') + + try : + validate_string(key) # Check if key is a string. If not, raise error. + except : + print('error in validating string keys') + print('string key passed : ' , key ) + + # Validate for keys that are numbers + num_keys = [("Bolt.Slip_Factor", True), # List of all parameters that are numbers (key, is_float) + ("Detailing.Gap", False), + ("Load.Axial", False), + ("Load.Shear", False), + ("Weld.Material_Grade_OverWrite", False)] + for key in num_keys: # Loop through all keys. + # Check if key is a number. If not, raise error. + print('validating num keys') + validate_num(key[0], key[1]) + + # Validate for keys that are arrays + arr_keys = [("Bolt.Diameter", False), # List of all parameters that can be converted to numbers (key, is_float) + ("Bolt.Grade", True), + ("Connector.Plate.Thickness_List", False)] + for key in arr_keys: + print('validating arr key') + # Check if key is a list where all items can be converted to numbers. If not, raise error. + validate_arr(key[0], key[1]) + + +def create_module() -> FinPlateConnection: + """Create an instance of the FinPlateConnection module design class and set it up for use""" + print("\n[create_module] Creating FinPlateConnection instance...") + try: + module = FinPlateConnection() # Create an instance of the FinPlateConnection + print(f" ✅ FinPlateConnection instance created: {id(module)}") + + print(f" Setting logger with id='web'...") + module.set_osdaglogger(None, id="web") + print(f" ✅ Logger set successfully") + print(f" Logger name: {getattr(module.logger, 'name', 'N/A') if hasattr(module, 'logger') else 'No logger'}") + + return module + except Exception as e: + print(f" ❌ ERROR in create_module: {type(e).__name__}: {e}") + traceback.print_exc() + raise + + +def create_from_input(input_values: Dict[str, Any]) -> FinPlateConnection: + """Create an instance of the beam beam end plate connection module design class from input values.""" + print("\n" + "=" * 60) + print("create_from_input() called") + print("=" * 60) + print(f"Input values received: {len(input_values)} keys") + print(f"Sample keys: {list(input_values.keys())[:5]}") + + module = None + print("\n[create_from_input] Step 1: Creating module instance...") + try: + module = create_module() # Create module instance. + print(f"✅ Module created successfully: {type(module).__name__}") + print(f" Module ID: {id(module)}") + except Exception as e: + print(f"❌ ERROR in create_module: {type(e).__name__}: {e}") + print('error in creating module') + traceback.print_exc() + raise + + # Map frontend keys to osdag_core keys + # Frontend sends 'Connectivity' but osdag_core expects 'Connectivity *' (KEY_CONN) + print(f"\n[create_from_input] Step 2: Mapping frontend keys to osdag_core keys...") + print(f" KEY_CONN constant value: '{KEY_CONN}'") + print(f" 'Connectivity' in input_values: {'Connectivity' in input_values}") + print(f" KEY_CONN in input_values: {KEY_CONN in input_values}") + + design_dictionary = input_values.copy() + if 'Connectivity' in design_dictionary and KEY_CONN not in design_dictionary: + connectivity_value = design_dictionary.pop('Connectivity') + design_dictionary[KEY_CONN] = connectivity_value + print(f" ✅ Mapped 'Connectivity' -> '{KEY_CONN}'") + print(f" Connectivity value: '{connectivity_value}'") + else: + print(f" ℹ️ No mapping needed (Connectivity already mapped or not present)") + if KEY_CONN in design_dictionary: + print(f" KEY_CONN value: '{design_dictionary[KEY_CONN]}'") + + print(f" Final design_dictionary has {len(design_dictionary)} keys") + print(f" Key check - KEY_CONN present: {KEY_CONN in design_dictionary}") + + # Set the input values on the module instance. + print(f"\n[create_from_input] Step 3: Setting input values on module...") + print(f" Module is None: {module is None}") + try: + if module is None: + raise RuntimeError('Module instance was not created') + + print(f" Calling module.set_input_values() with {len(design_dictionary)} keys...") + print(f" Sample keys being passed: {list(design_dictionary.keys())[:5]}") + + module.set_input_values(design_dictionary) + print(f" ✅ set_input_values() completed successfully") + + # Verify key was set correctly + if hasattr(module, 'connectivity'): + print(f" ✅ Module connectivity attribute: '{module.connectivity}'") + else: + print(f" ⚠️ Module has no 'connectivity' attribute") + + except Exception as e: + print(f"\n❌ ERROR in set_input_values:") + print(f" Exception type: {type(e).__name__}") + print(f" Exception message: {str(e)}") + print(f" Design dictionary keys: {list(design_dictionary.keys())[:10]}") + print(f" KEY_CONN in design_dictionary: {KEY_CONN in design_dictionary}") + if KEY_CONN in design_dictionary: + print(f" KEY_CONN value: '{design_dictionary[KEY_CONN]}'") + traceback.print_exc() + print('error in setting the input values') + raise + + print("=" * 60) + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """ + Generate, format and return the input values from the given output values. + Output format (json): { + "Bolt.Pitch": + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)" + "val": 40 + } + } + """ + print("\n" + "=" * 60) + print("generate_output() called") + print("=" * 60) + print(f"Input values keys (first 10): {list(input_values.keys())[:10]}") + + output = {} # Dictionary for formatted values + + print("\n[Step 1] Creating module from input...") + try: + module = create_from_input(input_values) # Create module from input. + print(f"✅ Module created: {type(module).__name__}") + print(f"Module object: {module}") + except Exception as e: + print(f"Failed to create module: {e}") + raise + + # Initialize logs variable first + logs = [] + + try: + print("\n[Step 2] Calling module.output_values(True)...") + print(f" Module type: {type(module)}") + print(f" Module has output_values method: {hasattr(module, 'output_values')}") + try: + raw_output_text = module.output_values(True) + print(f"✅ output_values() returned {len(raw_output_text)} parameters") + if len(raw_output_text) > 0: + print(f" First parameter sample: {raw_output_text[0] if isinstance(raw_output_text[0], (list, tuple)) else 'N/A'}") + except Exception as e: + print(f"❌ ERROR in output_values(): {type(e).__name__}: {e}") + traceback.print_exc() + raise + + print("\n[Step 3] Calling module.spacing(True)...") + print(f" Module has spacing method: {hasattr(module, 'spacing')}") + try: + raw_output_spacing = module.spacing(True) + print(f"✅ spacing() returned {len(raw_output_spacing)} parameters") + if len(raw_output_spacing) > 0: + print(f" First spacing parameter: {raw_output_spacing[0] if isinstance(raw_output_spacing[0], (list, tuple)) else 'N/A'}") + except Exception as e: + print(f"❌ ERROR in spacing(): {type(e).__name__}: {e}") + print(f" Error details: {str(e)}") + traceback.print_exc() + raise + + print("\n[Step 4] Calling module.capacities(True)...") + print(f" Module has capacities method: {hasattr(module, 'capacities')}") + try: + raw_output_capacities = module.capacities(True) + print(f"✅ capacities() returned {len(raw_output_capacities)} parameters") + if len(raw_output_capacities) > 0: + print(f" First capacity parameter: {raw_output_capacities[0] if isinstance(raw_output_capacities[0], (list, tuple)) else 'N/A'}") + except Exception as e: + print(f"❌ ERROR in capacities(): {type(e).__name__}: {e}") + traceback.print_exc() + raise + + print("\n[Step 5] Calling module.section_capacities(True)...") + print(f" Module has section_capacities method: {hasattr(module, 'section_capacities')}") + try: + raw_output_section_capacities = module.section_capacities(True) + print(f"✅ section_capacities() returned {len(raw_output_section_capacities)} parameters") + if len(raw_output_section_capacities) > 0: + print(f" First section_capacity parameter: {raw_output_section_capacities[0] if isinstance(raw_output_section_capacities[0], (list, tuple)) else 'N/A'}") + except Exception as e: + print(f"❌ ERROR in section_capacities(): {type(e).__name__}: {e}") + traceback.print_exc() + raise + + print("\n[Step 6] Retrieving logs from logger...") + # Get logs from the custom logger + if hasattr(module, 'logger'): + print(f" Logger exists: {type(module.logger)}") + print(f" Logger name: {getattr(module.logger, 'name', 'N/A')}") + if isinstance(module.logger, CustomLogger): + try: + logs = module.logger.get_logs() + print(f" ✅ Retrieved {len(logs) if logs else 0} logs from custom logger") + if logs and len(logs) > 0: + print(f" First log entry: {logs[0] if isinstance(logs[0], (str, dict)) else 'N/A'}") + except Exception as e: + print(f" ⚠️ Error getting logs: {e}") + logs = [] + else: + print(f" ⚠️ Logger is not CustomLogger instance, type: {type(module.logger)}") + logs = [] + else: + print(" ⚠️ Module has no logger attribute") + logs = [] + + print("\n[Step 7] Combining all raw outputs...") + print(f" raw_output_text length: {len(raw_output_text)}") + print(f" raw_output_spacing length: {len(raw_output_spacing)}") + print(f" raw_output_capacities length: {len(raw_output_capacities)}") + print(f" raw_output_section_capacities length: {len(raw_output_section_capacities)}") + raw_output = raw_output_text + raw_output_spacing + raw_output_capacities + raw_output_section_capacities + print(f"✅ Combined raw_output length: {len(raw_output)}") + + print("\n[Step 8] Processing parameters into output dict...") + processed_count = 0 + skipped_count = 0 + + # Process each parameter with handling for both 4 and 5 element tuples + for i, param in enumerate(raw_output): + if i < 5 or i % 50 == 0: # Print first 5 and every 50th + print(f" Processing param {i}/{len(raw_output)}: {param[:2] if len(param) >= 2 else param}") + + # Handle both 4-element and 5-element tuples + if len(param) >= 4: + key = param[0] + label = param[1] + param_type = param[2] + value = param[3] + + # Check if it's a TextBox type and has a valid key + if param_type == "TextBox" and key is not None: + # Handle numpy types + if hasattr(value, 'item'): # numpy scalar + value = value.item() + + output[key] = { + "key": key, + "label": label, + "val": value + } + processed_count += 1 + else: + skipped_count += 1 + if i < 5: # Only print details for first few + print(f" ⚠️ Skipped: type={param_type}, key={key}") + + print(f"✅ Processed {processed_count} parameters, skipped {skipped_count}") + except Exception as e: + print("\n" + "=" * 60) + print("ERROR in generate_output()") + print("=" * 60) + print(f"Exception type: {type(e).__name__}") + print(f"Exception message: {str(e)}") + print(f"Exception args: {e.args if hasattr(e, 'args') else 'N/A'}") + + print(f"\nModule state at error:") + if 'module' in locals(): + print(f" Module exists: {module is not None}") + if module: + print(f" Module type: {type(module)}") + print(f" Module has logger: {hasattr(module, 'logger')}") + if hasattr(module, 'connectivity'): + print(f" Module connectivity: {module.connectivity}") + if hasattr(module, 'design_status'): + print(f" Module design_status: {module.design_status}") + + # Check if exception has error attribute + if hasattr(e, 'error'): + print(f"Exception has 'error' attribute: {e.error}") + if e.error is None: + print("⚠️ WARNING: Exception.error is None!") + + print(f"\nFull traceback:") + traceback.print_exc() + print("=" * 60) + raise + + print("\nFull traceback:") + import traceback + traceback.print_exc() + print("=" * 60) + + # Re-raise the exception so service layer can handle it + raise + + print("\n" + "=" * 60) + print("✅ generate_output() completed successfully") + print("=" * 60) + print(f"Final output dict has {len(output)} keys") + print(f"Output keys (first 10): {list(output.keys())[:10]}") + print(f"Logs count: {len(logs) if logs else 0}") + if logs: + print(f"First log entry (original order): {logs[0] if len(logs) > 0 else 'N/A'}") + print(f"Last log entry (original order): {logs[-1] if len(logs) > 0 else 'N/A'}") + + print("=" * 60) + + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + if section not in ("Model", "Beam", "Column", "Plate"): # Error checking: If section is valid. + raise InvalidInputTypeError( + "section", "'Model', 'Beam', 'Column' or 'Plate'") + module = create_from_input(input_values) # Create module from input. + print('module from input values : ' , module) + print('module.module value:', repr(module.module)) + # IMPORTANT: Ensure module.module is set to KEY_DISP_FINPLATE for CAD generation + # module.module might be overwritten by input dictionary value + if module.module != KEY_DISP_FINPLATE: + print(f'WARNING: module.module is {repr(module.module)}, setting to KEY_DISP_FINPLATE') + module.module = KEY_DISP_FINPLATE + # Object that will create the CAD model. + # CommonDesignLogic signature: (display, cad_widget, folder, connection, mainmodule) + # connection should be KEY_DISP_FINPLATE ('FinPlateConnection') for proper CAD generation + # Force use of KEY_DISP_FINPLATE since module.module might be set to input value (e.g., "FinPlateConnection") + # instead of the display constant ("FinPlateConnection") + connection_key = KEY_DISP_FINPLATE + print('Using connection key:', repr(connection_key)) + print('KEY_DISP_FINPLATE value:', repr(KEY_DISP_FINPLATE)) + try : + cld = CommonDesignLogic(None, None, '', connection_key, module.mainmodule) + print('CommonDesignLogic created, cdl.connection:', cld.connection) + except Exception as e : + print('error in cld e : ' , e) + traceback.print_exc() + raise # Re-raise to prevent using undefined cld + + try : + # Setup the calculations object for generating CAD model. + scc.setup_for_cad(cld, module) + except Exception as e : + traceback.print_exc() + print('Error in setting up cad e : ' , e) + raise # Re-raise to prevent using undefined cld + + # The section of the module that will be generated. + # Explicitly skip merged Model generation; only per-part files are needed. + if section == "Model": + print("[CAD Adapter] Skipping Model generation (per requirement: only per-part files)") + return None + + cld.component = section + print(f'[CAD Adapter] Setting cld.component = {section}') + + # Only per-part generation + part_names = [] + part_files = {} + compound_model = None + + try: + print(f'[CAD Adapter] Generating individual part: cld.component = {cld.component}') + model = cld.create2Dcad() + print(f'[CAD Adapter] Generated model type: {type(model)}') + except Exception as e : + print('Error in cld.create2Dcad() e : ' , e) + return False + + # check if the cad_models folder exists or not + # if no, then create one + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + + print('2d model : ' , model) + # os.system("clear") # clear the terminal + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + print('brep file path in create_cad_model : ' , file_path) + + try : + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) # Generate CAD Model + except Exception as e : + print('Writing to BREP file failed e : ' , e) + + # Export single STL next to BREP + try: + single_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), single_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save STL for {section}: {stle}") + + return file_path diff --git a/backend/apps/modules/shear_connection/submodules/fin_plate/service.py b/backend/apps/modules/shear_connection/submodules/fin_plate/service.py new file mode 100644 index 000000000..1da1f01f3 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/fin_plate/service.py @@ -0,0 +1,95 @@ +""" +Fin Plate Service - Business logic layer +Bridges between API and osdag_core +""" +from osdag_core.design_type.connection.fin_plate_connection import FinPlateConnection +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class FinPlateService: + """Service class for FinPlateConnection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """ + Run design calculation and return results. + + Args: + inputs: Dictionary of input parameters + request: Optional Django request object (for future use) + project_id: Optional project ID (for future use) + user_email: Optional user email (for future use) + + Returns: + Dictionary with 'data' (results) and 'logs' (calculation logs) + """ + print("=" * 60) + print("FinPlateService.calculate() called") + print("=" * 60) + print(f"Inputs received: {list(inputs.keys())[:10]}...") # Print first 10 keys + + try: + # Validate inputs + print("\n[1/3] Validating inputs...") + validate_input(inputs) + print("✅ Input validation passed") + + # Generate formatted output (this handles module creation and calculation) + print("\n[2/3] Generating output (creates module and runs calculation)...") + output, logs = generate_output(inputs) + print(f"✅ Output generated: {len(output)} output parameters") + print(f"✅ Logs retrieved: {len(logs) if logs else 0} log entries") + + print("\n[3/3] Preparing response...") + result = { + 'data': output, + 'logs': logs or [], # Ensure logs is always a list + 'success': True + } + print("✅ Response prepared successfully") + print("=" * 60) + + return result + + except Exception as e: + print("\n" + "=" * 60) + print("ERROR in FinPlateService.calculate()") + print("=" * 60) + print(f"Exception type: {type(e).__name__}") + print(f"Exception message: {str(e)}") + + # Safely extract error message + error_msg = str(e) + if hasattr(e, 'error') and e.error is not None: + error_msg = str(e.error) + elif hasattr(e, 'args') and len(e.args) > 0: + error_msg = str(e.args[0]) + + print(f"Final error message: {error_msg}") + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60) + + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': error_msg + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate ('Model', 'Beam', 'Column', 'Plate') + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/shear_connection/submodules/seated_angle/__init__.py b/backend/apps/modules/shear_connection/submodules/seated_angle/__init__.py new file mode 100644 index 000000000..f445b1e72 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/seated_angle/__init__.py @@ -0,0 +1,6 @@ + +""" +Seated Angle Connection Sub-module +""" +MODULE_ID = 'SeatedAngleConnection' +from .service import SeatedAngleService as Service \ No newline at end of file diff --git a/backend/apps/modules/shear_connection/submodules/seated_angle/adapter.py b/backend/apps/modules/shear_connection/submodules/seated_angle/adapter.py new file mode 100644 index 000000000..67cde4242 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/seated_angle/adapter.py @@ -0,0 +1,494 @@ +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type +) +from apps.modules.shear_connection import shared as scc +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.Common import KEY_DISP_SEATED_ANGLE, KEY_CONN +# Will log a lot of unnessecary data. +from osdag_core.design_type.connection.seated_angle_connection import SeatedAngleConnection +from osdag_core.custom_logger import CustomLogger +import sys +import os +from typing import Dict, Any, List +import traceback +from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh +from OCC.Core.StlAPI import StlAPI_Writer +try: + from OCC.Core.RWGltf import RWGltf_CafWriter + HAS_GLB = True +except Exception: + HAS_GLB = False + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + +def get_required_keys_seated_angle() -> List[str]: + return [ + "Bolt.Bolt_Hole_Type", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Slip_Factor", + "Bolt.TensionType", + "Bolt.Type", + "Connectivity", + "Connector.Material", + "Design.Design_Method", + "Detailing.Corrosive_Influences", + "Detailing.Edge_type", + "Detailing.Gap", + "Load.Shear", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab", + "Weld.Material_Grade_OverWrite", + "Connector.Angle_List", + "Connector.Top_Angle" + ] + +def validate_input(input_values: Dict[str,Any])-> None: + #check iif all required keys exist + required_keys = get_required_keys_seated_angle() + # check if input_values contains all required keys + missing_keys = contains_keys(input_values, required_keys) + if missing_keys != None: #if keys are missing. + #Raise error for the first missinf key. + raise MissingKeyError(missing_keys[0]) + + #check if Seated.Angle_Type is a string. + if not isinstance(input_values["Bolt.Bolt_Hole_Type"],str): + #if not raise an error + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type") + + #validate Bolt Diameter + bolt_diameter =input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter,list) + or not validate_list_type(bolt_diameter,str) + or not custom_list_validation(bolt_diameter, int_able)): + raise InvalidInputTypeError( + "Bolt.Diameter","non empty List[str] where all items can be converted to int" + ) + + # validate seated grade + bolt_grade = input_values["Bolt.Grade"] + if(not isinstance(bolt_grade,list) + or not validate_list_type(bolt_grade,str) + or not custom_list_validation(bolt_grade,float_able)): + + #if any condition fail raise an error + raise InvalidInputTypeError( + "Bolt.Grade", "non empty List[str] where all items can be converted to float" + ) + + #Validate seated.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor,str) + or not float_able(bolt_slipfactor)): + raise InvalidInputTypeError( + "Bolt.Slip_Factor", "str where str can be converted to float" + ) + + #Validate seated.TensionType + if not isinstance(input_values["Bolt.TensionType"], str): + # If not, raise error. + raise InvalidInputTypeError("Bolt.TensionType", "str") + + #Validate seated.Type + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") # If not, raise error. + + # validate connectivity + if not isinstance(input_values["Connectivity"], str): + # If not, raise error. + raise InvalidInputTypeError("Connectivity", "str") + + #Validate Connector Material + if not isinstance(input_values["Connector.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + # Check if Design.Design_Method is a string. + if not isinstance(input_values["Design.Design_Method"], str): + # If not, raise error. + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + # Check if Detailing.Corrosive_Influences is 'Yes' or 'No'. + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + # If not, raise error. + raise InvalidInputTypeError( + "Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + # Check if Detailing.Edge_type is a string. + if not isinstance(input_values["Detailing.Edge_type"], str): + # If not, raise error. + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) # Check if Detailing.Gap is a string. + or not int_able(detailing_gap)): # Check if Detailing.Gap can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Detailing.Gap", "str where str can be converted to int") + + + # Validate Load.Shear + load_shear = input_values["Load.Shear"] + if (not isinstance(load_shear, str) # Check if Load.Shear is a string. + or not int_able(load_shear)): # Check if Load.Shear can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Load.Shear", "str where str can be converted to int") + + # Validate Material + # Check if Material is a string. + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") # If not, raise error. + + # Validate Member.Supported_Section.Designation + # Check if Member.Supported_Section.Designation is a string. + if not isinstance(input_values["Member.Supported_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supported_Section.Designation", "str") + + # Validate Member.Supported_Section.Material + # Check if Member.Supported_Section.Material is a string. + if not isinstance(input_values["Member.Supported_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError("Member.Supported_Section.Material", "str") + + # Validate Member.Supporting_Section.Designation + # Check if Member.Supporting_Section.Designation is a string. + if not isinstance(input_values["Member.Supporting_Section.Designation"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Designation", "str") + + # Validate Member.Supporting_Section.Material + # Check if Member.Supporting_Section.Material is a string. + if not isinstance(input_values["Member.Supporting_Section.Material"], str): + # If not, raise error. + raise InvalidInputTypeError( + "Member.Supporting_Section.Material", "str") + + # Validate Module + # Check if Module is a string. + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") # If not, raise error. + + # Validate Weld.Fab + # Check if Weld.Fab is a string. + if not isinstance(input_values["Weld.Fab"], str): + raise InvalidInputTypeError("Weld.Fab", "str") # If not, raise error. + + # Validate Weld.Material_Grade_OverWrite + weld_materialgradeoverwrite = input_values["Weld.Material_Grade_OverWrite"] + if (not isinstance(weld_materialgradeoverwrite, str) # Check if Weld.Material_Grade_OverwWite is a string. + or not int_able(weld_materialgradeoverwrite)): # Check if Weld.Material_Grade_OverWrite can be converted to int. + # If any of these conditions fail, raise error. + raise InvalidInputTypeError( + "Weld.Material_Grade_OverWrite", "str where str can be converted to int.") + +def validate_input_new(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + + # Check if all required keys exist + required_keys = get_required_keys_seated_angle() + print('required_keys : ' , required_keys) + # Check if input_values contains all required keys. + missing_keys = contains_keys(input_values, required_keys) + print('missing keys : ' , missing_keys) + if missing_keys != None: # If keys are missing. + # Raise error for the first missing key. + print("missing keys is not None") + raise MissingKeyError(missing_keys[0]) + + + # Validate key types using loops. + + # Validate all strings. + str_keys = ["Bolt.Bolt_Hole_Type", # List of all parameters that are strings + "Bolt.TensionType", + "Bolt.Type", + "Bolt.Connectivity", + "Bolt.Connector_Material", + "Design.Design_Method", + "Detailing.Edge_type", + "Material", + "Member.Supported_Section.Designation", + "Member.Supported_Section.Material", + "Member.Supporting_Section.Designation", + "Member.Supporting_Section.Material", + "Module", + "Weld.Fab"] + for key in str_keys: # Loop through all keys. + print('validating string key') + + try : + validate_string(key) # Check if key is a string. If not, raise error. + except : + print('error in validating string keys') + print('string key passed : ' , key ) + + # Validate for keys that are numbers + num_keys = [("Bolt.Slip_Factor", True), # List of all parameters that are numbers (key, is_float) + ("Detailing.Gap", False), + ("Load.Shear", False), + ("Weld.Material_Grade_OverWrite", False)] + for key in num_keys: # Loop through all keys. + # Check if key is a number. If not, raise error. + print('validating num keys') + validate_num(key[0], key[1]) + + # Validate for keys that are arrays + arr_keys = [("Bolt.Diameter", False), # List of all parameters that can be converted to numbers (key, is_float) + ("Bolt.Grade", True),] + for key in arr_keys: + print('validating arr key') + # Check if key is a list where all items can be converted to numbers. If not, raise error. + validate_arr(key[0], key[1]) + +def create_module() -> SeatedAngleConnection: + """Create an instance of the FinPlateConnection module design class and set it up for use""" + module = SeatedAngleConnection() # Create an instance of the FinPlateConnection + module.set_osdaglogger(None, id="web") + return module + +def create_from_input(input_values: Dict[str, Any]) -> SeatedAngleConnection: + """Create an instance of the FinPlateConnection module design class from input values.""" + # validate_input(input_values) + try : + module = create_module() # Create module instance. + except Exception as e : + print('e in create_module : ' , e) + print('error in creating module') + + # Map frontend keys to osdag_core keys + # Frontend sends 'Connectivity' but osdag_core expects 'Connectivity *' (KEY_CONN) + design_dictionary = input_values.copy() + if 'Connectivity' in design_dictionary and KEY_CONN not in design_dictionary: + design_dictionary[KEY_CONN] = design_dictionary.pop('Connectivity') + + # Set the input values on the module instance. + try : + print('setting input values : ' , input_values) + module.set_input_values(design_dictionary) + except Exception as e : + traceback.print_exc() + print('e in set_input_values : ' , e) + print('error in setting the input values') + + return module + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """ + Generate, format and return the input values from the given output values. + Output format (json): { + "Bolt.Pitch": + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)" + "value": 40 + } + } + """ + output = {} # Dictionary for formatted values + module = create_from_input(input_values) # Create module from input. + + # Generate output values in unformatted form. + raw_output_text = module.output_values(True) + raw_output_capacities = module.capacities(True) + raw_output_seated_spacing_beam = module.seated_spacing_beam(True) + raw_output_seated_spacing_col = module.seated_spacing_col(True) + raw_output_top_spacing_col = module.top_spacing_col(True) + raw_output_top_spacing_beam = module.top_spacing_beam(True) + + raw_seated_spacing_beam = [ + (f"{key}_seated_beam", label, typ, value, visible if len(item) == 5 else True) + for item in raw_output_seated_spacing_beam + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_seated_spacing_col = [ + (f"{key}_seated_col", label, typ, value, visible if len(item) == 5 else True) + for item in raw_output_seated_spacing_col + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_top_spacing_col = [ + (f"{key}_top_col", label, typ, value, visible if len(item) == 5 else True) + for item in raw_output_top_spacing_col + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + raw_top_spacing_beam = [ + (f"{key}_top_beam", label, typ, value, visible if len(item) == 5 else True) + for item in raw_output_top_spacing_beam + if len(item) >= 4 and item[0] and item[2] == "TextBox" + for (key, label, typ, value, *rest) in [item] + for visible in [rest[0] if rest else True] + ] + + # Prefer CustomLogger logs if available + if hasattr(module, "logger") and isinstance(module.logger, CustomLogger): + logs = module.logger.get_logs() + else: + logs = module.logs if hasattr(module, "logs") else [] + raw_output = raw_output_text + raw_output_capacities + raw_seated_spacing_col + raw_seated_spacing_beam + raw_top_spacing_beam + raw_top_spacing_col + # os.system("clear") + # Loop over all the text values and add them to ouptut dict. + for param in raw_output: + if param[2] == "TextBox": # If the parameter is a text output, + key = param[0] # id/key + label = param[1] # label text. + value = param[3] # Value as string. + output[key] = { + "key": key, + "label": label, + "val": value # Changed from "value" to "val" to match frontend expectations + } # Set label, key and value in output + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + from apps.core.utils import write_stl + from OCC.Core.BRep import BRep_Builder + from OCC.Core.TopoDS import TopoDS_Compound + from OCC.Core.Message import Message_ProgressRange + + if section not in ("Model", "Beam", "Column", "SeatedAngle"): # Error checking: If section is valid. + raise InvalidInputTypeError( + "section", "'Model', 'Beam', 'Column' or 'SeatedAngle'") + module = create_from_input(input_values) # Create module from input. + # Object that will create the CAD model. + try : + cld = CommonDesignLogic(None, None, '', KEY_DISP_SEATED_ANGLE, module.mainmodule) + except Exception as e : + print('error in cld e : ' , e) + return False + + try : + # Setup the calculations object for generating CAD model. + scc.setup_for_cad(cld, module) + except Exception as e : + import traceback + traceback.print_exc() + print('Error in setting up cad e : ' , e) + + cld.component = section + + part_names = ["Beam", "Column", "SeatedAngle", "Weld", "Welds", "Bolt", "Bolts"] + try: + if section == "Model": + # Build compound by adding each part shape without fusing + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + for part in part_names: + try: + cld.component = part + part_shape = cld.create2Dcad() + if part_shape is None: + continue + builder.Add(compound, part_shape) + part_file_name = f"{session}_{part}.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + full_brep_path = os.path.join(os.getcwd(), part_file_path_rel) + from OCC.Core import BRepTools + BRepTools.breptools.Write(part_shape, full_brep_path, Message_ProgressRange()) + try: + module.logs.append(f"Wrote {part} BREP: {full_brep_path}") + except Exception: + pass + # STL as well + part_stl_file = part_file_path_rel.replace(".brep", ".stl") + try: + full_stl = os.path.join(os.getcwd(), part_stl_file) + write_stl(part_shape, full_stl) + try: + module.logs.append(f"Wrote {part} STL: {full_stl}") + except Exception: + pass + except Exception as e: + print(f"Failed to write STL for part {part} (SeatedAngle):", e) + except Exception as e: + print(f"Failed generating cad part {part} in SeatedAngle: {e}") + + # Now write the compound as the Model + cld.component = "Model" + model = compound + compound_file_name = f"{session}_Model.brep" + compound_file_path_rel = os.path.join("file_storage", "cad_models", compound_file_name) + from OCC.Core import BRepTools + full_compound = os.path.join(os.getcwd(), compound_file_path_rel) + BRepTools.breptools.Write(model, full_compound, Message_ProgressRange()) + try: + module.logs.append(f"Wrote Model BREP: {full_compound}") + except Exception: + pass + # Compound/model STL (for completeness, not loaded in UI) + compound_stl_file = compound_file_path_rel.replace(".brep", ".stl") + try: + full_compound_stl = os.path.join(os.getcwd(), compound_stl_file) + write_stl(model, full_compound_stl) + try: + module.logs.append(f"Wrote Model STL: {full_compound_stl}") + except Exception: + pass + except Exception as e: + print("Failed to write Model STL for SeatedAngle:", e) + return compound_file_path_rel + else: + try : + model = cld.create2Dcad() # Generate CAD Model. + except Exception as e : + print('Error in cld.create2Dcad() e : ' , e) + return False + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print('path does not exists cad_models , creating one') + os.makedirs(cad_models_path, exist_ok=True) + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + try : + from OCC.Core import BRepTools + full_brep = os.path.join(os.getcwd(), file_path) + BRepTools.breptools.Write(model, full_brep, Message_ProgressRange()) + try: + module.logs.append(f"Wrote {section} BREP: {full_brep}") + except Exception: + pass + # Write STL too + stl_file_path = file_path.replace(".brep", ".stl") + full_stl = os.path.join(os.getcwd(), stl_file_path) + write_stl(model, full_stl) + try: + module.logs.append(f"Wrote {section} STL: {full_stl}") + except Exception: + pass + except Exception as e : + print('Writing to BREP or STL file failed e : ' , e) + return file_path + except Exception as top_e: + print('Top-level error in SeatedAngle create_cad_model:', top_e) + return False + diff --git a/backend/apps/modules/shear_connection/submodules/seated_angle/service.py b/backend/apps/modules/shear_connection/submodules/seated_angle/service.py new file mode 100644 index 000000000..a16642a30 --- /dev/null +++ b/backend/apps/modules/shear_connection/submodules/seated_angle/service.py @@ -0,0 +1,26 @@ +""" +Seated Angle Service - Business logic layer +""" +from osdag_core.design_type.connection.seated_angle_connection import SeatedAngleConnection +from .adapter import validate_input, generate_output, create_cad_model + + +class SeatedAngleService: + """Service class for Seated Angle Connection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """Run design calculation and return results""" + validate_input(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs, + 'success': True + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """Generate CAD model and return file path""" + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/shear_connection/urls.py b/backend/apps/modules/shear_connection/urls.py new file mode 100644 index 000000000..e0fa6d72e --- /dev/null +++ b/backend/apps/modules/shear_connection/urls.py @@ -0,0 +1,12 @@ +""" +Shear Connection URLs +""" +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import ShearConnectionViewSet + +router = DefaultRouter() +router.register(r'', ShearConnectionViewSet, basename='shear-connection') + +urlpatterns = router.urls + diff --git a/backend/apps/modules/shear_connection/views.py b/backend/apps/modules/shear_connection/views.py new file mode 100644 index 000000000..b77b6918d --- /dev/null +++ b/backend/apps/modules/shear_connection/views.py @@ -0,0 +1,370 @@ +""" +Shear Connection ViewSet - Routes to sub-module services +Uses URL slug (not POST body) to find the correct service +Handles guest mode and optional project_id saving +""" +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from rest_framework import status +import traceback +from .registry import ShearConnectionRegistry +from apps.core.utils.module_helpers import handle_design_request +from apps.core.utils.cad_helpers import generate_cad_models, get_default_sections +from apps.core.models import Columns, Beams, Bolt, Material, CustomMaterials, Angles +from apps.core.api.design.report_customization_api import generate_initial_report_core + + +SHEAR_REPORT_MODULE_ID_MAP = { + "fin-plate": "FinPlateConnection", + "cleat-angle": "CleatAngleConnection", + "end-plate": "EndPlateConnection", + "seated-angle": "Seated-Angle-Connection", +} + + +class ShearConnectionViewSet(viewsets.ViewSet): + """ + Generic ViewSet that routes to specific sub-module services based on URL slug. + Supports guest mode and optional project_id saving for authenticated users. + """ + permission_classes = [AllowAny] # Allow both authenticated and guest users + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/design') + def design(self, request, submodule_slug=None): + """ + POST /api/modules/shear-connection/{submodule_slug}/design/ + + Request body: + { + "inputs": {...}, # Design input parameters + "project_id": 123 # Optional: Save results to project if user is authenticated + } + + Example: POST /api/modules/shear-connection/fin-plate/design/ + + Guest Mode: + - Can calculate designs + - Cannot save to projects (project_id is ignored) + + Authenticated Users: + - Can calculate designs + - Can save to projects if project_id is provided + """ + # Use URL slug to find service (not POST body) + ServiceClass = ShearConnectionRegistry.get_service_by_slug(submodule_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {submodule_slug} not found'}, + status=404 + ) + + # Extract inputs and optional project_id + inputs = request.data.get('inputs', request.data) # Support both formats + project_id = request.data.get('project_id') + + # Handle authentication and project saving (shared logic) + context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug=submodule_slug, + module_name='shear-connection' + ) + + try: + print("\n" + "=" * 80) + print(f"[ShearConnectionViewSet.design] slug={submodule_slug}") + print(f"[ShearConnectionViewSet.design] ServiceClass={getattr(ServiceClass, '__name__', ServiceClass)}") + print(f"[ShearConnectionViewSet.design] input keys={list(inputs.keys()) if isinstance(inputs, dict) else type(inputs)}") + print(f"[ShearConnectionViewSet.design] project_id={project_id}, guest={context.get('is_guest')}") + # Call the service with request context + result = ServiceClass.calculate( + inputs=inputs, + request=request, + project_id=project_id if not context['is_guest'] else None, + user_email=context['user_email'] + ) + print(f"[ShearConnectionViewSet.design] success result keys={list(result.keys()) if isinstance(result, dict) else type(result)}") + print("=" * 80 + "\n") + + # Add project saving result to response + if context['project_result']: + result['project_saved'] = context['project_result']['saved'] + if context['project_result'].get('project_id'): + result['project_id'] = context['project_result']['project_id'] + if context['project_result'].get('error'): + result['project_error'] = context['project_result']['error'] + + return Response(result, status=200) + except Exception as e: + print("\n" + "=" * 80) + print(f"[ShearConnectionViewSet.design] ERROR slug={submodule_slug}") + print(f"[ShearConnectionViewSet.design] exception={type(e).__name__}: {e}") + if isinstance(inputs, dict): + print(f"[ShearConnectionViewSet.design] inputs sample keys={list(inputs.keys())[:15]}") + for k in [ + "Connectivity", + "Bolt.Type", + "Member.Supported_Section.Designation", + "Member.Supporting_Section.Designation", + "Connector.Plate.Thickness_List", + "Module", + ]: + if k in inputs: + print(f"[ShearConnectionViewSet.design] {k}={inputs.get(k)!r}") + traceback.print_exc() + print("=" * 80 + "\n") + return Response( + {'error': str(e), 'success': False}, + status=400 + ) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/report/generate-initial') + def report_generate_initial(self, request, submodule_slug=None): + """ + POST /api/modules/shear-connection/{submodule_slug}/report/generate-initial/ + + Request body (slug determines module_id, no need to send it): + { + "metadata": {...}, # Already in backend format + "input_values": {...}, # Or "inputs": {...} + "design_status": boolean, + "logs": [...], + "sections": [...], # Optional + "customization": {...} # Optional + } + + Returns: + { + "success": true, + "report_id": "string", + "sections": {...}, + "message": "LaTeX report generated successfully" + } + """ + module_id = SHEAR_REPORT_MODULE_ID_MAP.get(submodule_slug) + if not module_id: + return Response( + { + "success": False, + "error": f"Report generation is not supported for shear-connection sub-module '{submodule_slug}'", + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + input_values = request.data.get("input_values") or request.data.get("inputs") + if not input_values: + return Response( + {"success": False, "error": "input_values are required"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + mapped_data = { + "module_id": module_id, + "input_values": input_values, + "metadata": request.data.get("metadata"), + "design_status": request.data.get("design_status", True), + "logs": request.data.get("logs", []), + } + + if "sections" in request.data: + mapped_data["sections"] = request.data.get("sections") + if "customization" in request.data: + mapped_data["customization"] = request.data.get("customization") + if "images" in request.data: + mapped_data["images"] = request.data.get("images") + + payload, status_code = generate_initial_report_core(mapped_data) + return Response(payload, status=status_code) + + @action(detail=False, methods=['get'], url_path='(?P[^/.]+)/options') + def options(self, request, submodule_slug=None): + """ + GET /api/modules/shear-connection/{submodule_slug}/options/ + Returns dropdown/options data for the sub-module. + """ + email = request.query_params.get("email") + slug = submodule_slug + + # Common data helpers + def material_list(): + mats = list(Material.objects.all().values()) + if email: + mats += list(CustomMaterials.objects.filter(email=email).values()) + mats.append({"id": -1, "Grade": "Custom"}) + return mats + + def bolt_diameters(): + lst = list(Bolt.objects.values_list('Bolt_diameter', flat=True)) + lst.sort() + return lst + + property_classes = ['3.6', '4.6', '4.8', '5.6', '5.8', '6.8', '8.8', '9.8', '10.9', '12.9'] + thickness_list = [ + '8', '10', '12', '14', '16', '18', '20', '22', '25', '28', '32', '36', '40', '45', '50', + '56', '63', '75', '80', '90', '100', '110', '120' + ] + connectivity_common = ['Column Flange-Beam-Web', 'Column Web-Beam-Web', 'Beam-Beam'] + + try: + if slug == 'fin-plate': + data = { + 'connectivityList': connectivity_common, + 'columnList': list(Columns.objects.values_list('Designation', flat=True)), + 'beamList': list(Beams.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + } + return Response(data, status=status.HTTP_200_OK) + + if slug == 'cleat-angle': + data = { + 'connectivityList': connectivity_common, + 'columnList': list(Columns.objects.values_list('Designation', flat=True)), + 'beamList': list(Beams.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'angleList': list(Angles.objects.values_list('Designation', flat=True)), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + } + return Response(data, status=status.HTTP_200_OK) + + if slug == 'end-plate': + data = { + 'connectivityList': connectivity_common, + 'boltTypeList': ['Bearing Bolt', 'Friction Grip Bolt'], + 'columnList': list(Columns.objects.values_list('Designation', flat=True)), + 'beamList': list(Beams.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + } + return Response(data, status=status.HTTP_200_OK) + + if slug == 'seated-angle': + data = { + 'connectivityList': ['Column Flange-Beam-Web', 'Column Web-Beam-Web'], + 'columnList': list(Columns.objects.values_list('Designation', flat=True)), + 'beamList': list(Beams.objects.values_list('Designation', flat=True)), + 'materialList': material_list(), + 'angleList': list(Angles.objects.values_list('Designation', flat=True)), + 'topAngleList': list(Angles.objects.values_list('Designation', flat=True)), + 'boltDiameterList': bolt_diameters(), + 'boltTypeList': ['Bearing Bolt', 'Friction Grip Bolt'], + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + } + return Response(data, status=status.HTTP_200_OK) + + return Response({'error': f'Sub-module {slug} not found'}, status=404) + except Exception as e: + return Response({'error': str(e)}, status=500) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/cad') + def cad(self, request, submodule_slug=None): + """ + POST /api/modules/shear-connection/{submodule_slug}/cad/ + + Request body: + { + "inputs": {...}, # Design input parameters + "sections": ["Model", "Beam", ...] # Optional: specific sections to generate + } + + Returns: + { + "status": "success", + "files": {section: base64_data, ...}, + "hover_dict": {...}, + "warnings": [...] + } + """ + # Get service from registry + ServiceClass = ShearConnectionRegistry.get_service_by_slug(submodule_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {submodule_slug} not found'}, + status=404 + ) + + # Extract inputs + inputs = request.data.get('inputs', request.data) + + if not inputs: + return Response( + {'error': 'inputs are required'}, + status=400 + ) + + # Get sections from request or use defaults + sections = request.data.get('sections') + if not sections: + sections = get_default_sections('shear-connection', submodule_slug) + + if not sections: + return Response( + {'error': f'No sections defined for {submodule_slug}'}, + status=400 + ) + + try: + # Import adapter to get create_from_input function for hover_dict + create_from_input_func = None + try: + if submodule_slug == 'fin-plate': + from .submodules.fin_plate.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'cleat-angle': + from .submodules.cleat_angle.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'end-plate': + from .submodules.end_plate.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'seated-angle': + from .submodules.seated_angle.adapter import create_from_input + create_from_input_func = create_from_input + except ImportError as e: + print(f"[ShearConnectionViewSet] Could not import create_from_input for {submodule_slug}: {e}") + + # Generate CAD models + result = generate_cad_models( + service_class=ServiceClass, + inputs=inputs, + sections=sections, + create_from_input_func=create_from_input_func + ) + + if not result['files']: + return Response( + { + 'status': 'error', + 'message': 'No CAD models were generated', + 'errors': result['warnings'] + }, + status=422 + ) + + return Response({ + 'status': 'success', + 'files': result['files'], + 'hover_dict': result['hover_dict'], + 'message': 'CAD models generated successfully', + 'warnings': result['warnings'] + }, status=201) + + except Exception as e: + print(f"[ShearConnectionViewSet] Error generating CAD: {e}") + import traceback + traceback.print_exc() + return Response( + {'error': str(e), 'status': 'error'}, + status=500 + ) + diff --git a/APP_CRASH/Appcrash/_extlibs/__init__.py b/backend/apps/modules/simple_connection/__init__.py similarity index 100% rename from APP_CRASH/Appcrash/_extlibs/__init__.py rename to backend/apps/modules/simple_connection/__init__.py diff --git a/backend/apps/modules/simple_connection/registry.py b/backend/apps/modules/simple_connection/registry.py new file mode 100644 index 000000000..f42782c22 --- /dev/null +++ b/backend/apps/modules/simple_connection/registry.py @@ -0,0 +1,17 @@ +""" +Simple Connection Registry - auto-discovers sub-modules +""" +import os +from apps.core.registry import BaseModuleRegistry + + +class SimpleConnectionRegistry(BaseModuleRegistry): + """Registry for simple-connection sub-modules""" + pass + + +# Auto-discover sub-modules (expects MODULE_ID and Service in each __init__.py) +_package_name = 'apps.modules.simple_connection.submodules' +_package_path = os.path.join(os.path.dirname(__file__), 'submodules') +SimpleConnectionRegistry.auto_discover(_package_name, _package_path) + diff --git a/backend/apps/modules/simple_connection/shared.py b/backend/apps/modules/simple_connection/shared.py new file mode 100644 index 000000000..75abe38a7 --- /dev/null +++ b/backend/apps/modules/simple_connection/shared.py @@ -0,0 +1,14 @@ +""" +Shared utilities for simple_connection modules +""" +from osdag_core.cad.common_logic import CommonDesignLogic + + +def setup_for_cad(cdl: CommonDesignLogic, module_class): + """Minimal setup helper used by simple_connection adapters. + + This mirrors the expectations in adapters: set the module_class/module_object + so downstream common logic can access them. + """ + cdl.module_class = module_class + cdl.module_object = module_class diff --git a/backend/apps/modules/simple_connection/shared_validation.py b/backend/apps/modules/simple_connection/shared_validation.py new file mode 100644 index 000000000..44015d777 --- /dev/null +++ b/backend/apps/modules/simple_connection/shared_validation.py @@ -0,0 +1,182 @@ +""" +Shared validation utilities for simple connection modules. +DRY approach to avoid code duplication across adapters. +""" +from typing import Dict, Any, List, Optional +from apps.core.utils import ( + MissingKeyError, InvalidInputTypeError, + contains_keys, validate_list_type, + custom_list_validation, float_able, int_able +) +from apps.core.utils.errors import RangeValidationError + + +class SimpleConnectionValidator: + """Configurable validator for simple connection modules""" + + def __init__(self, required_keys: List[str], module_name: str): + self.required_keys = required_keys + self.module_name = module_name + self.range_validations: List[Dict[str, Any]] = [] + + def add_range_validation(self, key: str, min_value: float = 0.0, + max_value: Optional[float] = None, + allow_zero: bool = False): + """Add range validation for a numeric field""" + self.range_validations.append({ + 'key': key, + 'min': min_value, + 'max': max_value, + 'allow_zero': allow_zero + }) + return self + + def validate(self, input_values: Dict[str, Any]) -> None: + """Run all validations""" + iv = dict(input_values or {}) + + # 1. Check required keys + missing = contains_keys(iv, self.required_keys) + if missing: + raise MissingKeyError(missing[0]) + + # 2. Validate bolt fields (if present) + self._validate_bolt_fields(iv) + + # 3. Validate weld fields (if present) + self._validate_weld_fields(iv) + + # 4. Validate numeric fields + self._validate_numeric_fields(iv, input_values) + + # 5. Range validations + self._validate_ranges(iv) + + def _validate_bolt_fields(self, iv: Dict[str, Any]) -> None: + """Validate bolt diameter and grade if present""" + if "Bolt.Diameter" in iv: + bolt_dia = iv.get("Bolt.Diameter") + if (not isinstance(bolt_dia, list) + or not validate_list_type(bolt_dia, str) + or not custom_list_validation(bolt_dia, int_able)): + raise InvalidInputTypeError( + "Bolt.Diameter", + "non empty List[str] convertible to int" + ) + + if "Bolt.Grade" in iv: + bolt_grade = iv.get("Bolt.Grade") + if (not isinstance(bolt_grade, list) + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): + raise InvalidInputTypeError( + "Bolt.Grade", + "non empty List[str] convertible to float" + ) + + def _validate_weld_fields(self, iv: Dict[str, Any]) -> None: + """Validate weld size if present""" + if "Weld.Size" in iv: + weld_size = iv.get("Weld.Size") + # Normalize single value to list + if isinstance(weld_size, (int, float, str)): + iv["Weld.Size"] = [str(weld_size)] + + if (not isinstance(iv.get("Weld.Size"), list) + or not validate_list_type(iv.get("Weld.Size"), str) + or not custom_list_validation(iv.get("Weld.Size"), float_able)): + raise InvalidInputTypeError( + "Weld.Size", + "non empty List[str] convertible to float" + ) + + def _validate_numeric_fields(self, iv: Dict[str, Any], input_values: Dict[str, Any]) -> None: + """Validate common numeric fields""" + numeric_keys = [ + "Plate1Thickness", "Plate2Thickness", + "PlateWidth", "Load.Axial", "Bolt.Slip_Factor" + ] + + for key in numeric_keys: + if key not in iv: + continue + + val = iv.get(key) + # Normalize list/tuple to first element + if isinstance(val, (list, tuple)) and val: + val = val[0] + # Allow numeric and coerce to str + if isinstance(val, (int, float)): + val = str(val) + + if not isinstance(val, str) or not float_able(val): + raise InvalidInputTypeError(key, "str convertible to float") + + # Update normalized value in both dicts + iv[key] = val + input_values[key] = val + + def _validate_ranges(self, iv: Dict[str, Any]) -> None: + """Validate numeric ranges""" + for rule in self.range_validations: + key = rule['key'] + if key not in iv: + continue + + val = iv[key] + # Normalize to float + if isinstance(val, str): + try: + num_value = float(val) + except ValueError: + continue # Type validation already caught this + elif isinstance(val, (int, float)): + num_value = float(val) + else: + continue # Type validation already caught this + + min_val = rule['min'] + max_val = rule.get('max') + allow_zero = rule.get('allow_zero', False) + + # Check minimum + if not allow_zero and num_value <= 0: + raise RangeValidationError( + key, num_value, min_val, max_val, allow_zero + ) + elif allow_zero and num_value < 0: + raise RangeValidationError( + key, num_value, min_val, max_val, allow_zero + ) + + if num_value < min_val: + raise RangeValidationError( + key, num_value, min_val, max_val, allow_zero + ) + + # Check maximum + if max_val is not None and num_value > max_val: + raise RangeValidationError( + key, num_value, min_val, max_val, allow_zero + ) + + +# Factory functions for common validators +def create_bolted_validator(required_keys: List[str], module_name: str): + """Create validator for bolted connections""" + return (SimpleConnectionValidator(required_keys, module_name) + .add_range_validation("PlateWidth", min_value=0.0, allow_zero=False) + .add_range_validation("Load.Axial", min_value=0.0, allow_zero=False) + .add_range_validation("Plate1Thickness", min_value=0.0, allow_zero=False) + .add_range_validation("Plate2Thickness", min_value=0.0, allow_zero=False) + .add_range_validation("Bolt.Slip_Factor", min_value=0.0, max_value=1.0, allow_zero=False)) + + +def create_welded_validator(required_keys: List[str], module_name: str): + """Create validator for welded connections""" + return (SimpleConnectionValidator(required_keys, module_name) + .add_range_validation("PlateWidth", min_value=0.0, allow_zero=False) + .add_range_validation("Load.Axial", min_value=0.0, allow_zero=False) + .add_range_validation("Plate1Thickness", min_value=0.0, allow_zero=False) + .add_range_validation("Plate2Thickness", min_value=0.0, allow_zero=False)) + diff --git a/backend/apps/modules/simple_connection/submodules/__init__.py b/backend/apps/modules/simple_connection/submodules/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/__init__.py @@ -0,0 +1 @@ + diff --git a/backend/apps/modules/simple_connection/submodules/butt_joint_bolted/__init__.py b/backend/apps/modules/simple_connection/submodules/butt_joint_bolted/__init__.py new file mode 100644 index 000000000..f54a87440 --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/butt_joint_bolted/__init__.py @@ -0,0 +1,2 @@ +MODULE_ID = 'ButtJointBolted' +from .service import MODULE_ID, Service \ No newline at end of file diff --git a/backend/apps/modules/simple_connection/submodules/butt_joint_bolted/adapter.py b/backend/apps/modules/simple_connection/submodules/butt_joint_bolted/adapter.py new file mode 100644 index 000000000..edfda7536 --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/butt_joint_bolted/adapter.py @@ -0,0 +1,311 @@ +""" +Adapter for Butt Joint Bolted module +""" +from typing import Dict, Any, List +import os +import json +import traceback +from osdag_core.design_type.connection.butt_joint_bolted import ButtJointBolted +from osdag_core.cad.common_logic import CommonDesignLogic +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from osdag_core.Common import KEY_DISP_BUTTJOINTBOLTED +from osdag_core.custom_logger import CustomLogger +from apps.core.utils import ( + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, validate_list_type, write_stl +) +from ...shared import setup_for_cad +from ...shared_validation import create_bolted_validator + +def get_required_keys() -> List[str]: + return [ + "Module", + "Material", + "Load.Axial", + "Plate1Thickness", + "Plate2Thickness", + "PlateWidth", + "ButtJoint.CoverPlate", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Type", + "Bolt.Bolt_Hole_Type", + "Bolt.Slip_Factor", + "Design.For", + ] + +# Create shared validator instance +_validator = create_bolted_validator(get_required_keys(), "ButtJointBolted") + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate input values using shared validator""" + _validator.validate(input_values) + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate output from input values""" + output = {} + logs = [] + + try: + module = ButtJointBolted() + module.set_osdaglogger(None, id="web") + + validate_input(input_values) + module.set_input_values(input_values) + + for runner in ("design", "design_main", "design_module", "run_design"): + design_fn = getattr(module, runner, None) + if callable(design_fn): + design_fn() + break + + # Collect outputs via core tuple APIs + mapped_output = {} + def map_tuple_list(tuple_list): + key_map = { + # Bolt details + "Bolt.Diameter_Provided": "Bolt.Diameter", + "Bolt.Grade_Provided": "Bolt.Grade_Provided", + "Bolt.Type_Provided": "Bolt.Type_Provided", + "Bolt.Shear": "Bolt.Shear", + "Bolt.Bearing": "Bolt.Bearing", + "Bolt.Capacity": "Bolt.Capacity", + # Bolt design + "Bolt.Total_Number": "Bolt.number", + "PackingPlate.Thickness": "PackingPlate thk", + "Bolt.Rows_Provided": "Bolt.Rows", + "Bolt.Columns_Provided": "Bolt.Cols", + "Utilization.Ratio": "Bolt.UtilizationRatio", + "Design.For": "Design For", + "Plate.BaseCapacity": "Plate.BaseCapacity", + "Plate.BaseUtilization": "Plate.BaseUtilization", + "Bolt.Utilization": "Bolt.Utilization", + "Bolt.Connection_Length": "Bolt.ConnLength", + "Bolt.Pitch": "Bolt.Pitch", + "Bolt.EndDist": "Bolt.EndDist", + "Bolt.Gauge": "Bolt.Gauge", + "Bolt.EdgeDist": "Bolt.EdgeDist", + } + label_map = { + "Bolt.Diameter": "Diameter (mm)", + "Bolt.Grade_Provided": "Property Class", + "Bolt.Type_Provided": "Type", + "Bolt.Shear": "Shear Capacity (kN)", + "Bolt.Bearing": "Bearing Capacity (kN)", + "Bolt.Capacity": "Capacity (kN)", + "Bolt.number": "Number of Bolts", + "PackingPlate thk": "Packing Plate Thickness (mm)", + "Bolt.Rows": "Rows of Bolts", + "Bolt.Cols": "Columns of Bolts", + "Bolt.UtilizationRatio": "Utilisation Ratio", + "Design For": "Design For", + "Plate.BaseCapacity": "Connected Plate Capacity (kN)", + "Plate.BaseUtilization": "Connected Plate Utilization", + "Bolt.Utilization": "Bolt Utilization", + "Bolt.ConnLength": "Length of Connection (mm)", + "Bolt.Pitch": "Pitch Distance (mm)", + "Bolt.EndDist": "End Distance (mm)", + "Bolt.Gauge": "Gauge Distance (mm)", + "Bolt.EdgeDist": "Edge Distance (mm)", + } + for tup in tuple_list or []: + if len(tup) < 4: + continue + src_key, label, param_type, val = tup[:4] + # Skip buttons, notes, section images, and callables (but include actual spacing values) + if param_type in ("OutButton", "Button", "Note", "Section", "Title", "Popup_Section") or callable(val): + continue + # Skip None keys (used for titles/notes) + if src_key is None: + continue + target_key = key_map.get(src_key, src_key) + display_label = label_map.get(target_key, label or target_key) + mapped_output[target_key] = {"key": target_key, "label": display_label, "val": val} + + if hasattr(module, "output_values"): + map_tuple_list(module.output_values(True)) + if hasattr(module, "spacing") and callable(getattr(module, "spacing", None)): + map_tuple_list(module.spacing(True)) + + # Spacing diagram expects PlateWidth; module has self.width from input + if "PlateWidth" not in mapped_output: + plate_width = None + if hasattr(module, "width") and module.width is not None: + try: + plate_width = float(module.width) + except (TypeError, ValueError): + pass + if plate_width is None: + plate_width = input_values.get("PlateWidth") + if plate_width is not None: + mapped_output["PlateWidth"] = {"key": "PlateWidth", "label": "Plate Width (mm)", "val": plate_width} + + # If nothing mapped and module exposes a raw output dict, keep it + if mapped_output: + output = mapped_output + elif hasattr(module, "get_output_values"): + output = module.get_output_values() + elif hasattr(module, "output"): + output = module.output + + # Get logs from the custom logger (same as fin_plate and butt_joint_welded) + logs = [] + if hasattr(module, 'logger'): + if isinstance(module.logger, CustomLogger): + try: + logs = module.logger.get_logs() + except Exception as e: + print(f"[ButtJointBolted] Error getting logs: {e}") + logs = [] + else: + logs = getattr(module, "logs", []) + else: + logs = getattr(module, "logs", []) + except Exception as e: + error_msg = f"Error in design: {str(e)}" + if 'logs' not in locals(): + logs = [] + logs.append(error_msg) + traceback.print_exc() + return output, logs + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path. + Desktop options: Model, Plate 1, Plate 2, Cover Plate, Bolts. + """ + print(f"[ButtJointBolted CAD] Starting CAD generation for section='{section}', session='{session}'") + valid_sections = ("Model", "Plate 1", "Plate 2", "Cover Plate", "Bolts") + if section not in valid_sections: + raise InvalidInputTypeError("section", "'Model', 'Plate 1', 'Plate 2', 'Cover Plate', 'Bolts'") + + print(f"[ButtJointBolted CAD] Creating module instance") + module = ButtJointBolted() + print(f"[ButtJointBolted CAD] Initializing logger") + module.set_osdaglogger(None, id="web") + print(f"[ButtJointBolted CAD] Setting input values") + module.set_input_values(input_values) + print(f"[ButtJointBolted CAD] Input values set successfully") + # Force correct display key for CAD, mirroring fin plate behavior + if getattr(module, "module", None) != KEY_DISP_BUTTJOINTBOLTED: + print(f"[CAD DEBUG] Adjusting module.module from {getattr(module, 'module', None)} to {KEY_DISP_BUTTJOINTBOLTED}") + module.module = KEY_DISP_BUTTJOINTBOLTED + + # Setup CAD helper (following fin_plate pattern) + print(f"[ButtJointBolted CAD] Initializing CommonDesignLogic") + try: + connection_key = KEY_DISP_BUTTJOINTBOLTED + folder = "" + print(f"[ButtJointBolted CAD] CommonDesignLogic params: connection={connection_key}, mainmodule={module.mainmodule}, folder='{folder}'") + cdl = CommonDesignLogic(None, None, folder, connection_key, module.mainmodule) + print(f"[ButtJointBolted CAD] CommonDesignLogic initialized successfully") + print(f"[ButtJointBolted CAD] cdl.mainmodule={cdl.mainmodule}, cdl.connection={cdl.connection}") + except Exception as e: + print(f'[ButtJointBolted CAD] Error initializing CommonDesignLogic: {e}') + traceback.print_exc() + return "" + + print(f"[ButtJointBolted CAD] Setting up CAD") + try: + setup_for_cad(cdl, module) + print(f"[ButtJointBolted CAD] CAD setup completed") + except Exception as e: + traceback.print_exc() + print(f'[ButtJointBolted CAD] Error in setup_for_cad: {e}') + + # Create CAD models first (populates cdl.assembly, cdl.plate1_model, etc.) + print(f"[ButtJointBolted CAD] Creating CAD models") + try: + # Unpack tuple and assign to cdl attributes (same as line 3221 in common_logic.py) + cdl.assembly, cdl.plate1_model, cdl.plate2_model, cdl.platec_model, cdl.platec2_model, cdl.bolt_models, cdl.nuts_models, cdl.packing1_model, cdl.packing2_model = cdl.createButtJointBoltedCAD() + print(f"[ButtJointBolted CAD] CAD models created and assigned successfully") + print(f"[ButtJointBolted CAD] assembly type: {type(cdl.assembly).__name__ if cdl.assembly else 'None'}") + print(f"[ButtJointBolted CAD] plate1_model type: {type(cdl.plate1_model).__name__ if cdl.plate1_model else 'None'}") + print(f"[ButtJointBolted CAD] platec_model type: {type(cdl.platec_model).__name__ if cdl.platec_model else 'None'}") + except Exception as exc: + print(f"[ButtJointBolted CAD] CAD build failed: {exc}") + traceback.print_exc() + return "" + + # Map desktop section names to create2Dcad() component names (Plate1, Plate2, Cover Plate, Connector) + section_to_component = { + "Plate 1": "Plate1", + "Plate 2": "Plate2", + "Cover Plate": "Cover Plate", + "Bolts": "Connector", + } + cdl.component = section_to_component.get(section, "") + + print(f"[ButtJointBolted CAD] Setting component='{cdl.component}' for section='{section}'") + + # Generate model using create2Dcad() + print(f"[ButtJointBolted CAD] Calling create2Dcad()") + try: + model = cdl.create2Dcad() + print(f"[ButtJointBolted CAD] Generated model type: {type(model).__name__ if model else 'None'}") + except Exception as e: + print(f"[ButtJointBolted CAD] Error in create2Dcad(): {e}") + traceback.print_exc() + return "" + + # create2Dcad() returns a list for Connector (bolts+nuts); compound them + if isinstance(model, (list, tuple)) and model: + print(f"[ButtJointBolted CAD] Model is a list/tuple with {len(model)} items, building compound") + from OCC.Core.BRep import BRep_Builder + from OCC.Core.TopoDS import TopoDS_Compound + builder = BRep_Builder() + comp = TopoDS_Compound() + builder.MakeCompound(comp) + for shape in model: + if shape: + builder.Add(comp, shape) + model = comp + print(f"[ButtJointBolted CAD] Compound model type: {type(model).__name__}") + + if model is None: + print(f"[ButtJointBolted CAD] No model generated for section={section}; skipping write.") + return "" + + # Create CAD directory + print(f"[ButtJointBolted CAD] Creating CAD directory") + cad_dir = os.path.join(os.getcwd(), "file_storage", "cad_models") + os.makedirs(cad_dir, exist_ok=True) + + section_safe = section.replace(" ", "_") + file_name = f"{session}_{section_safe}.brep" + file_path = os.path.join("file_storage", "cad_models", file_name) + print(f"[ButtJointBolted CAD] Target file path: {file_path}") + + # Write BREP file (following fin_plate pattern) + print(f"[ButtJointBolted CAD] Writing BREP file: {file_path}") + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) + print(f"[ButtJointBolted CAD] BREP file written successfully") + except Exception as e: + print(f"[ButtJointBolted CAD] Writing to BREP file failed: {e}") + traceback.print_exc() + return "" + + # Export STL next to BREP (following fin_plate pattern) + try: + single_stl_rel = file_path.replace(".brep", ".stl") + print(f"[ButtJointBolted CAD] Writing STL: {single_stl_rel}") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + print(f"[ButtJointBolted CAD] STL file saved at {os.path.join(os.getcwd(), single_stl_rel)}") + except Exception as stle: + print(f"[ButtJointBolted CAD] Warning: Failed to save STL for {section}: {stle}") + + print(f"[ButtJointBolted CAD] CAD generation completed successfully. Returning path: {file_path}") + return file_path + +def create_from_input(input_values: Dict[str, Any]) -> Any: + """Create module instance from input""" + module = ButtJointBolted() + module.set_osdaglogger(None, id="web") + validate_input(input_values) + module.set_input_values(input_values) + return module diff --git a/backend/apps/modules/simple_connection/submodules/butt_joint_bolted/service.py b/backend/apps/modules/simple_connection/submodules/butt_joint_bolted/service.py new file mode 100644 index 000000000..b7f01286d --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/butt_joint_bolted/service.py @@ -0,0 +1,45 @@ +""" +Service for Butt Joint Bolted +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +MODULE_ID = "ButtJointBolted" + + +class Service: + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + try: + validate_input(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs or [], + 'success': True + } + except Exception as exc: + traceback.print_exc() + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': str(exc), + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate ('Model', 'Column', 'Plate') + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/simple_connection/submodules/butt_joint_welded/__init__.py b/backend/apps/modules/simple_connection/submodules/butt_joint_welded/__init__.py new file mode 100644 index 000000000..a4cee1578 --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/butt_joint_welded/__init__.py @@ -0,0 +1,2 @@ +MODULE_ID = 'ButtJointWelded' +from .service import MODULE_ID, Service \ No newline at end of file diff --git a/backend/apps/modules/simple_connection/submodules/butt_joint_welded/adapter.py b/backend/apps/modules/simple_connection/submodules/butt_joint_welded/adapter.py new file mode 100644 index 000000000..32b3c1aa4 --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/butt_joint_welded/adapter.py @@ -0,0 +1,338 @@ +""" +Adapter for Butt Joint Welded module +""" +from typing import Dict, Any, List +import os +import json +import traceback +from osdag_core.design_type.connection.butt_joint_welded import ButtJointWelded +from osdag_core.cad.common_logic import CommonDesignLogic +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from osdag_core.Common import KEY_DISP_BUTTJOINTWELDED +from osdag_core.custom_logger import CustomLogger +from apps.core.utils import ( + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, validate_list_type, write_stl +) +from ...shared import setup_for_cad +from ...shared_validation import create_welded_validator + +def get_required_keys() -> List[str]: + return [ + "Module", + "Material", + "Load.Axial", + "Plate1Thickness", + "Plate2Thickness", + "PlateWidth", + "ButtJoint.CoverPlate", + "Weld.Size", + "Weld.Material_Grade_OverWrite", + "Weld.Fab", + "Design.For", + ] + +# Create shared validator instance +_validator = create_welded_validator(get_required_keys(), "ButtJointWelded") + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate required inputs for ButtJointWelded using shared validator.""" + iv = dict(input_values or {}) + # Provide legacy defaults for weld metadata if omitted by caller or empty string + if not iv.get("Weld.Material_Grade_OverWrite") or iv.get("Weld.Material_Grade_OverWrite", "").strip() == "": + iv["Weld.Material_Grade_OverWrite"] = "410" + if not iv.get("Weld.Fab") or iv.get("Weld.Fab", "").strip() == "": + iv["Weld.Fab"] = "Shop Weld" + if not iv.get("Weld.Type") or iv.get("Weld.Type", "").strip() == "": + iv["Weld.Type"] = iv.get("Weld.Fab", "Shop Weld") + # Persist defaults back to caller dict so downstream set_input_values sees them + input_values.update(iv) + # Use shared validator for validation + _validator.validate(input_values) + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate output from input values""" + output = {} + logs = [] + try: + module = ButtJointWelded() + module.set_osdaglogger(None, id="web") + + validate_input(input_values) + module.set_input_values(input_values) + + for runner in ("design", "design_main", "design_module", "run_design"): + design_fn = getattr(module, runner, None) + if callable(design_fn): + design_fn() + break + + mapped_output = {} + def map_tuple_list(tuple_list): + key_map = { + "Weld.Type": "Weld.Type", + "Weld.Size": "Weld.Size", + "Weld.EffLength": "Weld.EffLength", + "Utilisation Ratio": "Utilisation Ratio", + "No Cover Plate": "No Cover Plate", + "Width of Cover Plate": "Width of Cover Plate", + "Length of Cover Plate": "Length of Cover Plate", + "Thickness of Cover Plate": "Thickness of Cover Plate", + # Spacing details (for Butt Joint Welded) + "Bolt.Pitch": "Bolt.Pitch", + "Bolt.EndDist": "Bolt.EndDist", + "Bolt.Gauge": "Bolt.Gauge", + "Bolt.EdgeDist": "Bolt.EdgeDist", + } + label_map = { + "Weld.Type": "Type", + "Weld.Size": "Size (mm)", + "Weld.EffLength": "Eff.Length (mm)", + "Utilisation Ratio": "Utilisation Ratio", + "No Cover Plate": "No Cover Plate", + "Width of Cover Plate": "Width of Cover Plate", + "Length of Cover Plate": "Length of Cover Plate", + "Thickness of Cover Plate": "Thickness of Cover Plate", + # Spacing details + "Bolt.Pitch": "Pitch Distance (mm)", + "Bolt.EndDist": "End Distance (mm)", + "Bolt.Gauge": "Gauge Distance (mm)", + "Bolt.EdgeDist": "Edge Distance (mm)", + } + for tup in tuple_list or []: + if len(tup) < 4: + continue + src_key, label, param_type, val = tup[:4] + # Skip buttons, notes, section images, and callables (but include actual spacing values) + if param_type in ("OutButton", "Button", "Note", "Section", "Title", "Popup_Section") or callable(val): + continue + # Skip None keys (used for titles/notes) + if src_key is None: + continue + target_key = key_map.get(src_key, src_key) + display_label = label_map.get(target_key, label or target_key) + mapped_output[target_key] = {"key": target_key, "label": display_label, "val": val} + + if hasattr(module, "output_values"): + map_tuple_list(module.output_values(True)) + # Include spacing details for Butt Joint Welded (it has spacing() method in Osdag) + if hasattr(module, "spacing") and callable(getattr(module, "spacing", None)): + try: + map_tuple_list(module.spacing(True)) + except (TypeError, AttributeError, ImportError) as e: + # Resource file access may fail in web environment + print(f"[ButtJointWelded] Could not load spacing diagram: {e}") + # Continue without spacing diagram + + # Supplement with scalars if not already mapped + def add_scalar(src_attr, target_key, label): + if target_key in mapped_output: + return + if hasattr(module, src_attr): + mapped_output[target_key] = {"key": target_key, "label": label, "val": getattr(module, src_attr)} + + add_scalar("weld_size", "Weld.Size", "Size (mm)") + # Weld strength in Osdag is in kN (converted from N in output_values) + if hasattr(module, 'weld_strength') and module.weld_strength: + weld_strength_kn = round(module.weld_strength / 1000, 2) if module.weld_strength > 1000 else module.weld_strength + mapped_output["Weld.Strength"] = {"key": "Weld.Strength", "label": "Strength (kN)", "val": weld_strength_kn} + add_scalar("weld_length_effective", "Weld.EffLength", "Eff.Length (mm)") + add_scalar("design_for", "Design For", "Design For") + add_scalar("weld_length_provided", "Bolt.ConnLength", "Length of Connection (mm)") # reused key for length + + if mapped_output: + output = mapped_output + elif hasattr(module, "output_values_dict") and isinstance(module.output_values_dict, dict): + output = module.output_values_dict + else: + output = {} + + # Get logs from the custom logger (same as fin_plate) + logs = [] + if hasattr(module, 'logger'): + if isinstance(module.logger, CustomLogger): + try: + logs = module.logger.get_logs() + except Exception as e: + print(f"[ButtJointWelded] Error getting logs: {e}") + logs = [] + else: + logs = getattr(module, "logs", []) + else: + logs = getattr(module, "logs", []) + except Exception as e: + error_msg = f"Error in design: {str(e)}" + if 'logs' not in locals(): + logs = [] + logs.append(error_msg) + traceback.print_exc() + return output, logs + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path. + Desktop options: Model, Plate 1, Plate 2, Cover Plate, Welds. + """ + valid_sections = ("Model", "Plate 1", "Plate 2", "Cover Plate", "Welds") + if section not in valid_sections: + raise InvalidInputTypeError("section", "'Model', 'Plate 1', 'Plate 2', 'Cover Plate', 'Welds'") + + module = ButtJointWelded() + module.set_osdaglogger(None, id="web") + validate_input(input_values) + module.set_input_values(input_values) + if getattr(module, "module", None) != KEY_DISP_BUTTJOINTWELDED: + module.module = KEY_DISP_BUTTJOINTWELDED + + try: + connection_key = KEY_DISP_BUTTJOINTWELDED + mainmodule = getattr(module, "mainmodule", "Moment Connection") + folder = "" + cdl = CommonDesignLogic(None, '', folder, connection_key, mainmodule) + except Exception as e: + print('Error initializing CommonDesignLogic:', e) + return "" + + try: + setup_for_cad(cdl, module) + except Exception as e: + traceback.print_exc() + print('Error in setup_for_cad:', e) + return "" + + try: + (cdl.assembly, cdl.plate1_model, cdl.plate2_model, cdl.platec_model, cdl.platec2_model, + cdl.weld_models, cdl.packing1_model, cdl.packing2_model) = cdl.createButtJointWeldedCAD() + except Exception as e: + print(f"Error in createButtJointWeldedCAD: {e}") + traceback.print_exc() + return "" + + def _compound_shapes(shapes): + from OCC.Core.BRep import BRep_Builder + from OCC.Core.TopoDS import TopoDS_Compound + shapes = [s for s in (shapes if isinstance(shapes, (list, tuple)) else [shapes]) if s] + if not shapes: + return None + if len(shapes) == 1: + return shapes[0] + builder = BRep_Builder() + comp = TopoDS_Compound() + builder.MakeCompound(comp) + for s in shapes: + builder.Add(comp, s) + return comp + + model = None + part_files = {} + part_names = [] + + if section == "Model": + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + for shape in [cdl.plate1_model, cdl.plate2_model, cdl.platec_model, + cdl.platec2_model if getattr(cdl, 'platec2_model', None) else None, + cdl.packing1_model if getattr(cdl, 'packing1_model', None) else None, + cdl.packing2_model if getattr(cdl, 'packing2_model', None) else None] + (list(cdl.weld_models) if cdl.weld_models else []): + if shape: + builder.Add(compound, shape) + model = compound + part_names = ["Plate_1", "Plate_2", "Cover_Plate", "Welds"] + elif section == "Plate 1": + model = cdl.plate1_model + elif section == "Plate 2": + model = cdl.plate2_model + elif section == "Cover Plate": + cover_parts = [cdl.platec_model] + if getattr(cdl, 'platec2_model', None): + cover_parts.append(cdl.platec2_model) + if getattr(cdl, 'packing1_model', None): + cover_parts.append(cdl.packing1_model) + if getattr(cdl, 'packing2_model', None): + cover_parts.append(cdl.packing2_model) + model = _compound_shapes(cover_parts) + elif section == "Welds": + model = _compound_shapes(cdl.weld_models) + + if model is None: + print(f"[CAD DEBUG] No model generated for section={section}; skipping write.") + return "" + + cad_dir = os.path.join(os.getcwd(), "file_storage", "cad_models") + os.makedirs(cad_dir, exist_ok=True) + + section_safe = section.replace(" ", "_") + file_name = f"{session}_{section_safe}.brep" + file_path = os.path.join("file_storage", "cad_models", file_name) + + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) + + if section == "Model": + try: + manifest = { + "session": session, + "mergedBrep": file_path, + "parts": [{"name": name, "brepPath": part_files.get(name)} for name in part_names if part_files.get(name)] + } + for entry in manifest["parts"]: + if entry.get("brepPath"): + entry["stlPath"] = entry["brepPath"].replace(".brep", ".stl") + manifest_path = file_path.replace(".brep", ".parts.json") + full_manifest_path = os.path.join(os.getcwd(), manifest_path) + with open(full_manifest_path, "w", encoding="utf-8") as mf: + json.dump(manifest, mf) + except Exception as me: + print(f"Warning: Failed to write manifest: {me}") + + try: + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) != 1: + print("Warning: Failed to save STEP file") + except Exception as stepe: + print(f"Warning: STEP export failed: {stepe}") + + try: + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) != 1: + print("Warning: Failed to save IGES file") + except Exception as igee: + print(f"Warning: IGES export failed: {igee}") + + try: + merged_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), merged_stl_rel)) + except Exception as stle: + print(f"Warning: Failed to save merged STL: {stle}") + + except Exception as e: + print("Writing to BREP file failed:", e) + + if section != "Model": + try: + single_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + except Exception as stle: + print(f"Warning: Failed to save STL for {section}: {stle}") + + return file_path + +def create_from_input(input_values: Dict[str, Any]) -> Any: + """Create module instance from input""" + module = ButtJointWelded() + module.set_osdaglogger(None, id="web") + validate_input(input_values) + module.set_input_values(input_values) + return module diff --git a/backend/apps/modules/simple_connection/submodules/butt_joint_welded/service.py b/backend/apps/modules/simple_connection/submodules/butt_joint_welded/service.py new file mode 100644 index 000000000..adf5c82ce --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/butt_joint_welded/service.py @@ -0,0 +1,44 @@ +""" +Service for Butt Joint Welded +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +MODULE_ID = "ButtJointWelded" + + +class Service: + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + try: + validate_input(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs or [], + 'success': True + } + except Exception as exc: + traceback.print_exc() + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': str(exc), + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate ('Model', 'Plate 1', 'Plate 2', 'Cover Plate', 'Welds') + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) \ No newline at end of file diff --git a/backend/apps/modules/simple_connection/submodules/lap_joint_bolted/__init__.py b/backend/apps/modules/simple_connection/submodules/lap_joint_bolted/__init__.py new file mode 100644 index 000000000..3c13470eb --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/lap_joint_bolted/__init__.py @@ -0,0 +1,2 @@ +MODULE_ID = 'LapJointBolted' +from .service import MODULE_ID, Service \ No newline at end of file diff --git a/backend/apps/modules/simple_connection/submodules/lap_joint_bolted/adapter.py b/backend/apps/modules/simple_connection/submodules/lap_joint_bolted/adapter.py new file mode 100644 index 000000000..6692b73b4 --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/lap_joint_bolted/adapter.py @@ -0,0 +1,420 @@ +""" +Adapter for Lap Joint Bolted module +""" +from typing import Dict, Any, List +import os +import json +import traceback +from osdag_core.design_type.connection.lap_joint_bolted import LapJointBolted +from osdag_core.cad.common_logic import CommonDesignLogic +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse +from osdag_core.Common import KEY_DISP_LAPJOINTBOLTED +from osdag_core.custom_logger import CustomLogger +from apps.core.utils import ( + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, validate_list_type, write_stl +) +from ...shared import setup_for_cad +from ...shared_validation import create_bolted_validator + +def get_required_keys() -> List[str]: + return [ + "Module", + "Material", + "Load.Axial", + "Plate1Thickness", + "Plate2Thickness", + "PlateWidth", + "Bolt.Diameter", + "Bolt.Grade", + "Bolt.Type", + "Bolt.Bolt_Hole_Type", + "Bolt.Slip_Factor", + "Design.For", + ] + +# Create shared validator instance +_validator = create_bolted_validator(get_required_keys(), "LapJointBolted") + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate input values using shared validator""" + _validator.validate(input_values) + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate output from input values""" + output = {} + logs = [] + try: + module = LapJointBolted() + module.set_osdaglogger(None, id="web") + + validate_input(input_values) + module.set_input_values(input_values) + + for runner in ("design", "design_main", "design_module", "run_design"): + design_fn = getattr(module, runner, None) + if callable(design_fn): + design_fn() + break + + mapped_output = {} + def map_tuple_list(tuple_list): + key_map = { + "Bolt.Diameter_Provided": "Bolt.Diameter", + "Bolt.Grade_Provided": "Bolt.Grade_Provided", + "Bolt.Type_Provided": "Bolt.Type_Provided", + "Bolt.Shear": "Bolt.Shear", + "Bolt.Bearing": "Bolt.Bearing", + "Bolt.Capacity": "Bolt.Capacity", + "Bolt.Total_Number": "Bolt.number", + "PackingPlate.Thickness": "PackingPlate thk", + "Bolt.Rows_Provided": "Bolt.Rows", + "Bolt.Columns_Provided": "Bolt.Cols", + "Utilization.Ratio": "Bolt.UtilizationRatio", + "Design.For": "Design For", + "Plate.BaseCapacity": "Plate.BaseCapacity", + "Plate.BaseUtilization": "Plate.BaseUtilization", + "Bolt.Utilization": "Bolt.Utilization", + "Bolt.Connection_Length": "Bolt.ConnLength", + "Bolt.Pitch": "Bolt.Pitch", + "Bolt.EndDist": "Bolt.EndDist", + "Bolt.Gauge": "Bolt.Gauge", + "Bolt.EdgeDist": "Bolt.EdgeDist", + "Member.Depth": "PlateWidth", + } + label_map = { + "Bolt.Diameter": "Diameter (mm)", + "Bolt.Grade_Provided": "Property Class", + "Bolt.Type_Provided": "Type", + "Bolt.Shear": "Shear Capacity (kN)", + "Bolt.Bearing": "Bearing Capacity (kN)", + "Bolt.Capacity": "Capacity (kN)", + "Bolt.number": "Number of Bolts", + "PackingPlate thk": "Packing Plate Thickness (mm)", + "Bolt.Rows": "Rows of Bolts", + "Bolt.Cols": "Columns of Bolts", + "Bolt.UtilizationRatio": "Utilisation Ratio", + "Design For": "Design For", + "Plate.BaseCapacity": "Base Metal Capacity (kN)", + "Plate.BaseUtilization": "Base Metal Utilization", + "Bolt.Utilization": "Bolt Utilization", + "Bolt.ConnLength": "Length of Connection (mm)", + "Bolt.Pitch": "Pitch Distance (mm)", + "Bolt.EndDist": "End Distance (mm)", + "Bolt.Gauge": "Gauge Distance (mm)", + "Bolt.EdgeDist": "Edge Distance (mm)", + "PlateWidth": "Member Depth (mm)", + } + for tup in tuple_list or []: + if len(tup) < 4: + continue + src_key, label, param_type, val = tup[:4] + param_type_lower = str(param_type).lower() if param_type else "" + if param_type in ("OutButton", "Button", "Note", "Section", "Title", "Popup_Section") or "button" in param_type_lower or callable(val): + continue + if src_key is None: + continue + target_key = key_map.get(src_key, src_key) + display_label = label_map.get(target_key, label or target_key) + mapped_output[target_key] = {"key": target_key, "label": display_label, "val": val} + + if hasattr(module, "output_values"): + map_tuple_list(module.output_values(True)) + if hasattr(module, "spacing") and callable(getattr(module, "spacing", None)): + map_tuple_list(module.spacing(True)) + + if mapped_output: + output = mapped_output + elif hasattr(module, "get_output_values"): + output = module.get_output_values() + elif hasattr(module, "output"): + output = module.output + + # Get logs from the custom logger (same as fin_plate and butt_joint_welded) + logs = [] + if hasattr(module, 'logger'): + if isinstance(module.logger, CustomLogger): + try: + logs = module.logger.get_logs() + except Exception as e: + print(f"[LapJointBolted] Error getting logs: {e}") + logs = [] + else: + logs = getattr(module, "logs", []) + else: + logs = getattr(module, "logs", []) + except Exception as e: + error_msg = f"Error in design: {str(e)}" + if 'logs' not in locals(): + logs = [] + logs.append(error_msg) + traceback.print_exc() + return output, logs + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + print(f"[LapJointBolted CAD] Starting CAD generation for section='{section}', session='{session}'") + # Two separate plate options: Plate 1 and Plate 2 (each returns only that plate's geometry) + valid_sections = ("Model", "Plate 1", "Plate 2", "Bolts", "Plate", "Bolt", "Connector") + if section not in valid_sections: + raise InvalidInputTypeError("section", "'Model', 'Plate 1', 'Plate 2', 'Bolts'") + + print(f"[LapJointBolted CAD] Creating module instance") + module = LapJointBolted() + print(f"[LapJointBolted CAD] Initializing logger") + module.set_osdaglogger(None, id="web") + print(f"[LapJointBolted CAD] Setting input values") + module.set_input_values(input_values) + print(f"[LapJointBolted CAD] Input values set successfully") + if getattr(module, "module", None) != KEY_DISP_LAPJOINTBOLTED: + print(f"[CAD DEBUG] Adjusting module.module from {getattr(module, 'module', None)} to {KEY_DISP_LAPJOINTBOLTED}") + module.module = KEY_DISP_LAPJOINTBOLTED + + print(f"[LapJointBolted CAD] Initializing CommonDesignLogic") + try: + connection_key = KEY_DISP_LAPJOINTBOLTED + mainmodule = "Moment Connection" + folder = "" + print(f"[LapJointBolted CAD] CommonDesignLogic params: connection={connection_key}, mainmodule={mainmodule}, folder='{folder}'") + cdl = CommonDesignLogic(None, '', folder, connection_key, mainmodule) + print(f"[LapJointBolted CAD] CommonDesignLogic initialized successfully") + except Exception as e: + print(f'[LapJointBolted CAD] Error initializing CommonDesignLogic: {e}') + traceback.print_exc() + return "" + + print(f"[LapJointBolted CAD] Setting up CAD") + try: + setup_for_cad(cdl, module) + print(f"[LapJointBolted CAD] CAD setup completed") + except Exception as e: + traceback.print_exc() + print(f'[LapJointBolted CAD] Error in setup_for_cad: {e}') + + # Call CAD method directly (bypassing create2Dcad since it doesn't have a branch for LapJointBolted) + print(f"[LapJointBolted CAD] Calling createBoltedLapJoint()") + try: + lap_joint, plate1, plate2, bolts, nuts = cdl.createBoltedLapJoint() + print(f"[LapJointBolted CAD] CAD components created -> lap_joint:{type(lap_joint)}, plate1:{type(plate1)}, plate2:{type(plate2)}, bolts:{type(bolts)}, nuts:{type(nuts)}") + except Exception as exc: + print(f"[LapJointBolted CAD] CAD build failed: {exc}") + traceback.print_exc() + return "" + + # Helper to flatten lists of shapes + def _flatten_shapes(items): + flat = [] + for m in items if isinstance(items, (list, tuple)) else [items]: + if isinstance(m, (list, tuple)): + flat.extend([x for x in m if x]) + elif m: + flat.append(m) + return flat + + # Helper to fuse shapes + def _fuse_shapes(shapes): + shapes = [s for s in shapes if s] + if not shapes: + return None + if len(shapes) == 1: + return shapes[0] + fused = shapes[0] + for s in shapes[1:]: + fused = BRepAlgoAPI_Fuse(fused, s).Shape() + return fused + + # Helper to create compound + def _compound_shapes(shapes): + shapes = [s for s in shapes if s] + if not shapes: + return None + builder = BRep_Builder() + comp = TopoDS_Compound() + builder.MakeCompound(comp) + for s in shapes: + builder.Add(comp, s) + return comp + + # Route components based on section + print(f"[LapJointBolted CAD] Processing section: {section}") + part_files = {} + model = None + + print(f"[LapJointBolted CAD] Building model for section: {section}") + try: + if section == "Model": + print(f"[LapJointBolted CAD] Building compound Model") + # Combine all parts for Model + fused_main = _fuse_shapes([p for p in [lap_joint, plate1, plate2] if p]) + bolts_comp = _compound_shapes(_flatten_shapes([bolts, nuts])) + + if fused_main and bolts_comp: + model = BRepAlgoAPI_Fuse(fused_main, bolts_comp).Shape() + elif fused_main: + model = fused_main + elif bolts_comp: + model = bolts_comp + + # Write Plate part (combined plate1 + plate2) + if plate1 or plate2: + print(f"[LapJointBolted CAD] Writing Plate part") + plate_combined = _fuse_shapes([p for p in [plate1, plate2] if p]) + if plate_combined: + part_file_name = f"{session}_Plate.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + print(f"[LapJointBolted CAD] Writing Plate BREP: {part_file_path_rel}") + BRepTools.breptools.Write(plate_combined, part_file_path_rel, Message_ProgressRange()) + part_files["Plate"] = part_file_path_rel + try: + part_stl_rel = part_file_path_rel.replace(".brep", ".stl") + print(f"[LapJointBolted CAD] Writing Plate STL: {part_stl_rel}") + write_stl(plate_combined, os.path.join(os.getcwd(), part_stl_rel)) + except Exception as stle: + print(f"[LapJointBolted CAD] Failed to write STL for Plate: {stle}") + + # Write Bolts part + if bolts_comp: + print(f"[LapJointBolted CAD] Writing Bolts part") + part_file_name = f"{session}_Bolts.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + print(f"[LapJointBolted CAD] Writing Bolts BREP: {part_file_path_rel}") + BRepTools.breptools.Write(bolts_comp, part_file_path_rel, Message_ProgressRange()) + part_files["Bolts"] = part_file_path_rel + try: + part_stl_rel = part_file_path_rel.replace(".brep", ".stl") + print(f"[LapJointBolted CAD] Writing Bolts STL: {part_stl_rel}") + write_stl(bolts_comp, os.path.join(os.getcwd(), part_stl_rel)) + except Exception as stle: + print(f"[LapJointBolted CAD] Failed to write STL for Bolts: {stle}") + + elif section == "Plate 1": + print(f"[LapJointBolted CAD] Building Plate 1 section (first plate only)") + model = plate1 + print(f"[LapJointBolted CAD] Plate 1 model type: {type(model).__name__ if model else 'None'}") + elif section == "Plate 2": + print(f"[LapJointBolted CAD] Building Plate 2 section (second plate only)") + model = plate2 + print(f"[LapJointBolted CAD] Plate 2 model type: {type(model).__name__ if model else 'None'}") + elif section == "Plate": + print(f"[LapJointBolted CAD] Building Plate section (combined plates)") + model = _fuse_shapes([p for p in [plate1, plate2] if p]) + print(f"[LapJointBolted CAD] Plate model type: {type(model).__name__ if model else 'None'}") + + elif section in ("Bolt", "Bolts", "Connector"): + print(f"[LapJointBolted CAD] Building {section} section (bolts/nuts)") + # Return bolts/nuts as compound + model = _compound_shapes(_flatten_shapes([bolts, nuts])) + print(f"[LapJointBolted CAD] {section} model type: {type(model).__name__ if model else 'None'}") + + else: + print(f"[LapJointBolted CAD] Building default section (lap_joint)") + # Default: return lap_joint + model = lap_joint + print(f"[LapJointBolted CAD] Default model type: {type(model).__name__ if model else 'None'}") + + except Exception as e: + print(f"[LapJointBolted CAD] Error processing CAD components: {e}") + traceback.print_exc() + return "" + + print(f"[LapJointBolted CAD] Creating CAD directory") + cad_dir = os.path.join(os.getcwd(), "file_storage", "cad_models") + os.makedirs(cad_dir, exist_ok=True) + + # Safe filename: replace spaces so "Plate 1" -> "Plate_1" + section_safe = section.replace(" ", "_") + file_name = f"{session}_{section_safe}.brep" + file_path = os.path.join("file_storage", "cad_models", file_name) + print(f"[LapJointBolted CAD] Target file path: {file_path}") + + if model is None: + print(f"[LapJointBolted CAD] No model generated for section={section}; skipping write.") + return "" + + print(f"[LapJointBolted CAD] Writing BREP file: {file_path}") + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) + print(f"[LapJointBolted CAD] BREP file written successfully") + + if section == "Model": + print(f"[LapJointBolted CAD] Generating additional formats for Model") + try: + manifest = { + "session": session, + "mergedBrep": file_path, + "parts": [{"name": name, "brepPath": part_files.get(name)} for name in part_files.keys()] + } + for entry in manifest["parts"]: + if entry.get("brepPath"): + entry["stlPath"] = entry["brepPath"].replace(".brep", ".stl") + manifest_path = file_path.replace(".brep", ".parts.json") + full_manifest_path = os.path.join(os.getcwd(), manifest_path) + with open(full_manifest_path, "w", encoding="utf-8") as mf: + json.dump(manifest, mf) + print(f"[LapJointBolted CAD] Manifest written: {manifest_path}") + except Exception as me: + print(f"[LapJointBolted CAD] Warning: Failed to write manifest: {me}") + + try: + print(f"[LapJointBolted CAD] Exporting STEP format") + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) != 1: + print(f"[LapJointBolted CAD] Warning: Failed to save STEP file") + else: + print(f"[LapJointBolted CAD] STEP file written: {step_file_path}") + except Exception as stepe: + print(f"[LapJointBolted CAD] Warning: STEP export failed: {stepe}") + + try: + print(f"[LapJointBolted CAD] Exporting IGES format") + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) != 1: + print(f"[LapJointBolted CAD] Warning: Failed to save IGES file") + else: + print(f"[LapJointBolted CAD] IGES file written: {iges_file_path}") + except Exception as igee: + print(f"[LapJointBolted CAD] Warning: IGES export failed: {igee}") + + try: + merged_stl_rel = file_path.replace(".brep", ".stl") + print(f"[LapJointBolted CAD] Writing merged STL: {merged_stl_rel}") + write_stl(model, os.path.join(os.getcwd(), merged_stl_rel)) + print(f"[LapJointBolted CAD] Merged STL written successfully") + except Exception as stle: + print(f"[LapJointBolted CAD] Warning: Failed to save merged STL: {stle}") + + except Exception as e: + print(f"[LapJointBolted CAD] Writing to BREP file failed: {e}") + traceback.print_exc() + + if section != "Model": + try: + single_stl_rel = file_path.replace(".brep", ".stl") + print(f"[LapJointBolted CAD] Writing single STL: {single_stl_rel}") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + print(f"[LapJointBolted CAD] Single STL written successfully") + except Exception as stle: + print(f"[LapJointBolted CAD] Warning: Failed to save STL for {section}: {stle}") + + print(f"[LapJointBolted CAD] CAD generation completed successfully. Returning path: {file_path}") + return file_path + +def create_from_input(input_values: Dict[str, Any]) -> Any: + """Create module instance from input""" + module = LapJointBolted() + module.set_osdaglogger(None, id="web") + validate_input(input_values) + module.set_input_values(input_values) + return module diff --git a/backend/apps/modules/simple_connection/submodules/lap_joint_bolted/service.py b/backend/apps/modules/simple_connection/submodules/lap_joint_bolted/service.py new file mode 100644 index 000000000..21c228372 --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/lap_joint_bolted/service.py @@ -0,0 +1,45 @@ +""" +Service for Lap Joint Bolted +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +MODULE_ID = "LapJointBolted" + + +class Service: + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + try: + validate_input(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs or [], + 'success': True + } + except Exception as exc: + traceback.print_exc() + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': str(exc), + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate ('Model', 'Column', 'Plate', 'Bolt', 'Bolts', 'Connector') + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/simple_connection/submodules/lap_joint_welded/__init__.py b/backend/apps/modules/simple_connection/submodules/lap_joint_welded/__init__.py new file mode 100644 index 000000000..60a63ca88 --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/lap_joint_welded/__init__.py @@ -0,0 +1,2 @@ +MODULE_ID = 'LapJointWelded' +from .service import MODULE_ID, Service \ No newline at end of file diff --git a/backend/apps/modules/simple_connection/submodules/lap_joint_welded/adapter.py b/backend/apps/modules/simple_connection/submodules/lap_joint_welded/adapter.py new file mode 100644 index 000000000..6dde75f12 --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/lap_joint_welded/adapter.py @@ -0,0 +1,310 @@ +""" +Adapter for Lap Joint Welded module +""" +from typing import Dict, Any, List +import os +import json +import traceback +from osdag_core.design_type.connection.lap_joint_welded import LapJointWelded +from osdag_core.cad.common_logic import CommonDesignLogic +from OCC.Core import BRepTools +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from osdag_core.Common import KEY_DISP_LAPJOINTWELDED +from osdag_core.custom_logger import CustomLogger +from apps.core.utils import ( + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, validate_list_type, write_stl +) +from ...shared import setup_for_cad +from ...shared_validation import create_welded_validator + +def get_required_keys() -> List[str]: + return [ + "Module", + "Material", + "Load.Axial", + "Plate1Thickness", + "Plate2Thickness", + "PlateWidth", + "Weld.Size", + "Weld.Material_Grade_OverWrite", + "Weld.Fab", + "Design.For", + ] + +# Create shared validator instance +_validator = create_welded_validator(get_required_keys(), "LapJointWelded") + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate input values using shared validator""" + iv = dict(input_values or {}) + # Provide legacy defaults for weld metadata if omitted by caller or empty string + if not iv.get("Weld.Material_Grade_OverWrite") or iv.get("Weld.Material_Grade_OverWrite", "").strip() == "": + iv["Weld.Material_Grade_OverWrite"] = "410" + if not iv.get("Weld.Fab") or iv.get("Weld.Fab", "").strip() == "": + iv["Weld.Fab"] = "Shop Weld" + if not iv.get("Weld.Type") or iv.get("Weld.Type", "").strip() == "": + iv["Weld.Type"] = iv.get("Weld.Fab", "Shop Weld") + input_values.update(iv) + # Use shared validator for validation + _validator.validate(input_values) + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate output from input values""" + output = {} + logs = [] + try: + module = LapJointWelded() + module.set_osdaglogger(None, id="web") + + validate_input(input_values) + module.set_input_values(input_values) + + for runner in ("design", "design_main", "design_module", "run_design"): + design_fn = getattr(module, runner, None) + if callable(design_fn): + design_fn() + break + + mapped_output = {} + def map_tuple_list(tuple_list): + key_map = { + "Weld.Type": "Weld.Type", + "Weld.Size": "Weld.Size", + "Weld.EffLength": "Weld.EffLength", + "Utilisation Ratio": "Utilisation Ratio", + "No Cover Plate": "No Cover Plate", + "Width of Cover Plate": "Width of Cover Plate", + "Length of Cover Plate": "Length of Cover Plate", + "Thickness of Cover Plate": "Thickness of Cover Plate", + } + label_map = { + "Weld.Type": "Type", + "Weld.Size": "Size (mm)", + "Weld.EffLength": "Eff.Length (mm)", + "Utilisation Ratio": "Utilisation Ratio", + "No Cover Plate": "No Cover Plate", + "Width of Cover Plate": "Width of Cover Plate", + "Length of Cover Plate": "Length of Cover Plate", + "Thickness of Cover Plate": "Thickness of Cover Plate", + } + for tup in tuple_list or []: + if len(tup) < 4: + continue + src_key, label, param_type, val = tup[:4] + param_type_lower = str(param_type).lower() + if "button" in param_type_lower or callable(val): + continue + target_key = key_map.get(src_key, src_key) + display_label = label_map.get(target_key, label or target_key) + mapped_output[target_key] = {"key": target_key, "label": display_label, "val": val} + + if hasattr(module, "output_values"): + map_tuple_list(module.output_values(True)) + # Do not call spacing for welded (spacing() references self.plate and crashes) + + # Supplement with scalars if not already mapped + def add_scalar(src_attr, target_key, label): + if target_key in mapped_output: + return + if hasattr(module, src_attr): + mapped_output[target_key] = {"key": target_key, "label": label, "val": getattr(module, src_attr)} + + add_scalar("weld_size", "Weld.Size", "Size (mm)") + # Weld strength in Osdag is in kN (converted from N in output_values) + if hasattr(module, 'weld_strength') and module.weld_strength: + weld_strength_kn = round(module.weld_strength / 1000, 2) if module.weld_strength > 1000 else module.weld_strength + mapped_output["Weld.Strength"] = {"key": "Weld.Strength", "label": "Strength (kN)", "val": weld_strength_kn} + add_scalar("weld_length_effective", "Weld.EffLength", "Eff.Length (mm)") + add_scalar("design_for", "Design For", "Design For") + add_scalar("connection_length", "Bolt.ConnLength", "Length of Connection (mm)") + + if mapped_output: + output = mapped_output + elif hasattr(module, "output_values_dict") and isinstance(module.output_values_dict, dict): + output = module.output_values_dict + else: + output = {} + + # Get logs from the custom logger (same as fin_plate and butt_joint_welded) + logs = [] + if hasattr(module, 'logger'): + if isinstance(module.logger, CustomLogger): + try: + logs = module.logger.get_logs() + except Exception as e: + print(f"[LapJointWelded] Error getting logs: {e}") + logs = [] + else: + logs = getattr(module, "logs", []) + else: + logs = getattr(module, "logs", []) + except Exception as e: + error_msg = f"Error in design: {str(e)}" + if 'logs' not in locals(): + logs = [] + logs.append(error_msg) + traceback.print_exc() + return output, logs + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path. + Desktop options: Model, Plate 1, Plate 2, Welds. + """ + valid_sections = ("Model", "Plate 1", "Plate 2", "Welds") + if section not in valid_sections: + raise InvalidInputTypeError("section", "'Model', 'Plate 1', 'Plate 2', 'Welds'") + + module = LapJointWelded() + module.set_osdaglogger(None, id="web") + validate_input(input_values) + module.set_input_values(input_values) + if getattr(module, "module", None) != KEY_DISP_LAPJOINTWELDED: + module.module = KEY_DISP_LAPJOINTWELDED + + try: + connection_key = KEY_DISP_LAPJOINTWELDED + mainmodule = getattr(module, "mainmodule", "Moment Connection") + folder = "" + cdl = CommonDesignLogic(None, '', folder, connection_key, mainmodule) + except Exception as e: + print('Error initializing CommonDesignLogic:', e) + return "" + + try: + setup_for_cad(cdl, module) + except Exception as e: + traceback.print_exc() + print('Error in setup_for_cad:', e) + return "" + + try: + cdl.assembly, cdl.plate1_model, cdl.plate2_model, cdl.weld_models = cdl.createWeldedLapJoint() + except Exception as e: + print(f"Error in createWeldedLapJoint: {e}") + traceback.print_exc() + return "" + + def _compound_shapes(shapes): + from OCC.Core.BRep import BRep_Builder + shapes = [s for s in (shapes if isinstance(shapes, (list, tuple)) else [shapes]) if s] + if not shapes: + return None + if len(shapes) == 1: + return shapes[0] + builder = BRep_Builder() + comp = TopoDS_Compound() + builder.MakeCompound(comp) + for s in shapes: + builder.Add(comp, s) + return comp + + model = None + part_files = {} + part_names = [] + + if section == "Model": + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + for shape in [cdl.plate1_model, cdl.plate2_model] + (list(cdl.weld_models) if cdl.weld_models else []): + if shape: + builder.Add(compound, shape) + model = compound + part_names = ["Plate_1", "Plate_2", "Welds"] + for pname, pshape in [("Plate_1", cdl.plate1_model), ("Plate_2", cdl.plate2_model), ("Welds", _compound_shapes(cdl.weld_models))]: + if pshape: + pref = os.path.join("file_storage", "cad_models", f"{session}_{pname}.brep") + part_files[pname] = pref + try: + BRepTools.breptools.Write(pshape, pref, Message_ProgressRange()) + write_stl(pshape, os.path.join(os.getcwd(), pref.replace(".brep", ".stl"))) + except Exception as ex: + print(f"Warning: failed to write part {pname}: {ex}") + elif section == "Plate 1": + model = cdl.plate1_model + elif section == "Plate 2": + model = cdl.plate2_model + elif section == "Welds": + model = _compound_shapes(cdl.weld_models) + + if model is None: + print(f"[CAD DEBUG] No model generated for section={section}; skipping write.") + return "" + + cad_dir = os.path.join(os.getcwd(), "file_storage", "cad_models") + os.makedirs(cad_dir, exist_ok=True) + + section_safe = section.replace(" ", "_") + file_name = f"{session}_{section_safe}.brep" + file_path = os.path.join("file_storage", "cad_models", file_name) + + try: + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) + + if section == "Model": + try: + manifest = { + "session": session, + "mergedBrep": file_path, + "parts": [{"name": name, "brepPath": part_files.get(name)} for name in part_names if part_files.get(name)] + } + for entry in manifest["parts"]: + if entry.get("brepPath"): + entry["stlPath"] = entry["brepPath"].replace(".brep", ".stl") + manifest_path = file_path.replace(".brep", ".parts.json") + full_manifest_path = os.path.join(os.getcwd(), manifest_path) + with open(full_manifest_path, "w", encoding="utf-8") as mf: + json.dump(manifest, mf) + except Exception as me: + print(f"Warning: Failed to write manifest: {me}") + + try: + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) != 1: + print("Warning: Failed to save STEP file") + except Exception as stepe: + print(f"Warning: STEP export failed: {stepe}") + + try: + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) != 1: + print("Warning: Failed to save IGES file") + except Exception as igee: + print(f"Warning: IGES export failed: {igee}") + + try: + merged_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), merged_stl_rel)) + except Exception as stle: + print(f"Warning: Failed to save merged STL: {stle}") + + except Exception as e: + print("Writing to BREP file failed:", e) + + if section != "Model": + try: + single_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + except Exception as stle: + print(f"Warning: Failed to save STL for {section}: {stle}") + + return file_path + +def create_from_input(input_values: Dict[str, Any]) -> Any: + """Create module instance from input""" + module = LapJointWelded() + module.set_osdaglogger(None, id="web") + validate_input(input_values) + module.set_input_values(input_values) + return module diff --git a/backend/apps/modules/simple_connection/submodules/lap_joint_welded/service.py b/backend/apps/modules/simple_connection/submodules/lap_joint_welded/service.py new file mode 100644 index 000000000..a566f0d3a --- /dev/null +++ b/backend/apps/modules/simple_connection/submodules/lap_joint_welded/service.py @@ -0,0 +1,45 @@ +""" +Service for Lap Joint Welded +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +MODULE_ID = "LapJointWelded" + + +class Service: + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + try: + validate_input(inputs) + output, logs = generate_output(inputs) + return { + 'data': output, + 'logs': logs or [], + 'success': True + } + except Exception as exc: + traceback.print_exc() + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': str(exc), + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate ('Model', 'Plate 1', 'Plate 2', 'Welds') + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/simple_connection/urls.py b/backend/apps/modules/simple_connection/urls.py new file mode 100644 index 000000000..98836e4ed --- /dev/null +++ b/backend/apps/modules/simple_connection/urls.py @@ -0,0 +1,8 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import SimpleConnectionViewSet + +router = DefaultRouter() +router.register(r'', SimpleConnectionViewSet, basename='simple-connection') + +urlpatterns = router.urls diff --git a/backend/apps/modules/simple_connection/views.py b/backend/apps/modules/simple_connection/views.py new file mode 100644 index 000000000..a1176ed18 --- /dev/null +++ b/backend/apps/modules/simple_connection/views.py @@ -0,0 +1,327 @@ +""" +Simple Connection ViewSet - routes to sub-module services via slug. +Mirrors shear_connection pattern (design + options endpoints). +""" +import logging +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.permissions import AllowAny +from rest_framework.response import Response + +from apps.core.models import Material, CustomMaterials, Bolt +from apps.core.utils.module_helpers import handle_design_request +from apps.core.utils.cad_helpers import generate_cad_models, get_default_sections +from apps.core.utils.errors import format_error_response, get_error_status_code +from apps.core.api.design.report_customization_api import generate_initial_report_core +from .registry import SimpleConnectionRegistry + +logger = logging.getLogger(__name__) + + +# Mapping from simple-connection slug to legacy report module_id (currently none implemented) +SIMPLE_CONNECTION_REPORT_MODULE_ID_MAP = { + # Example (when report support is added): + # "butt-joint-bolted": "Simple-Connection-Butt-Joint-Bolted-Report", + # "butt-joint-welded": "Simple-Connection-Butt-Joint-Welded-Report", +} + + +class SimpleConnectionViewSet(viewsets.ViewSet): + permission_classes = [AllowAny] + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/design') + def design(self, request, submodule_slug=None): + """ + POST /api/modules/simple-connection/{slug}/design/ + Request body supports {inputs: {...}} or raw dict for backward compatibility. + """ + ServiceClass = SimpleConnectionRegistry.get_service_by_slug(submodule_slug) + if not ServiceClass: + return Response({'error': f'Sub-module {submodule_slug} not found'}, status=404) + + inputs = request.data.get('inputs', request.data) + project_id = request.data.get('project_id') + + context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug=submodule_slug, + module_name='simple-connection' + ) + + try: + result = ServiceClass.calculate( + inputs=inputs, + request=request, + project_id=project_id if not context['is_guest'] else None, + user_email=context['user_email'] + ) + + if context['project_result']: + result['project_saved'] = context['project_result']['saved'] + if context['project_result'].get('project_id'): + result['project_id'] = context['project_result']['project_id'] + if context['project_result'].get('error'): + result['project_error'] = context['project_result']['error'] + + status_code = 200 if result.get('success', True) else 400 + return Response(result, status=status_code) + except Exception as exc: + logger.error(f"Error in simple connection design for {submodule_slug}: {exc}", exc_info=True) + error_response = format_error_response(exc) + status_code = get_error_status_code(exc) + return Response(error_response, status=status_code) + + @action(detail=False, methods=['get'], url_path='(?P[^/.]+)/options') + def options(self, request, submodule_slug=None): + """ + GET /api/modules/simple-connection/{slug}/options/ + Provides dropdown/options data for simple connections. + """ + email = request.query_params.get("email") + slug = submodule_slug + + def material_list(): + mats = list(Material.objects.all().values()) + if email: + mats += list(CustomMaterials.objects.filter(email=email).values()) + mats.append({"id": -1, "Grade": "Custom"}) + return mats + + def bolt_diameters(): + lst = list(Bolt.objects.values_list('Bolt_diameter', flat=True)) + lst.sort() + # frontend expects strings convertible to numbers + return [str(x) for x in lst] + + property_classes = ['3.6', '4.6', '4.8', '5.6', '5.8', '6.8', '8.8', '9.8', '10.9', '12.9'] + thickness_list = [ + '8', '10', '12', '14', '16', '18', '20', '22', '25', '28', '32', '36', '40', '45', '50', + '56', '63', '75', '80', '90', '100', '110', '120' + ] + cover_plate_list = ['Single-Cover', 'Double-Cover'] + weld_size_list = ['4', '5', '6', '8', '10', '12'] + + # Design preferences options + bolt_type_list = ['Non Pre-tensioned', 'Pre-tensioned'] + bolt_hole_type_list = ['Standard', 'Over-sized'] + slip_factor_list = ['0.3', '0.45', '0.5'] + edge_type_list = ['Sheared or hand flame cut', 'Rolled, machine-flame cut, sawn and planed'] + weld_type_list = ['Shop weld', 'Field weld'] + design_for_list = ['Tension', 'Compression'] + packing_plate_list = ['Yes', 'No'] + + try: + if slug in ('butt-joint-bolted', 'lap-joint-bolted'): + data = { + 'materialList': material_list(), + 'thicknessList': thickness_list, + 'coverPlateList': cover_plate_list, + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + # Design preferences + 'designPreferences': { + 'bolt': { + 'boltType': bolt_type_list, + 'boltHoleType': bolt_hole_type_list, + 'slipFactor': slip_factor_list, + }, + 'detailing': { + 'edgeType': edge_type_list, + }, + 'design': { + 'designFor': design_for_list, + } + } + } + return Response(data, status=status.HTTP_200_OK) + + if slug in ('butt-joint-welded', 'lap-joint-welded'): + data = { + 'materialList': material_list(), + 'thicknessList': thickness_list, + 'coverPlateList': cover_plate_list, + 'weldSizeList': weld_size_list, + # Design preferences + 'designPreferences': { + 'weld': { + 'weldType': weld_type_list, + # weldMaterialGradeOverwrite is a text input (Fu in MPa) + }, + 'detailing': { + 'edgeType': edge_type_list, + 'packingPlate': packing_plate_list if slug == 'butt-joint-welded' else None, + }, + 'design': { + 'designFor': design_for_list, + } + } + } + return Response(data, status=status.HTTP_200_OK) + + return Response({'error': f'Sub-module {slug} not found'}, status=404) + except Exception as exc: + return Response({'error': str(exc)}, status=500) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/cad') + def cad(self, request, submodule_slug=None): + """ + POST /api/modules/simple-connection/{submodule_slug}/cad/ + + Request body: + { + "inputs": {...}, # Design input parameters + "sections": ["Model", "Column", ...] # Optional: specific sections to generate + } + + Returns: + { + "status": "success", + "files": {section: base64_data, ...}, + "hover_dict": {...}, + "warnings": [...] + } + """ + # Get service from registry + ServiceClass = SimpleConnectionRegistry.get_service_by_slug(submodule_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {submodule_slug} not found'}, + status=404 + ) + + # Extract inputs + inputs = request.data.get('inputs', request.data) + + if not inputs: + return Response( + {'error': 'inputs are required'}, + status=400 + ) + + # Get sections from request or use defaults + sections = request.data.get('sections') + if not sections: + sections = get_default_sections('simple-connection', submodule_slug) + + if not sections: + return Response( + {'error': f'No sections defined for {submodule_slug}'}, + status=400 + ) + + try: + # Import adapter to get create_from_input function for hover_dict + create_from_input_func = None + try: + if submodule_slug == 'butt-joint-bolted': + from .submodules.butt_joint_bolted.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'butt-joint-welded': + from .submodules.butt_joint_welded.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'lap-joint-bolted': + from .submodules.lap_joint_bolted.adapter import create_from_input + create_from_input_func = create_from_input + elif submodule_slug == 'lap-joint-welded': + from .submodules.lap_joint_welded.adapter import create_from_input + create_from_input_func = create_from_input + except ImportError as e: + print(f"[SimpleConnectionViewSet] Could not import create_from_input for {submodule_slug}: {e}") + + # Generate CAD models + result = generate_cad_models( + service_class=ServiceClass, + inputs=inputs, + sections=sections, + create_from_input_func=create_from_input_func + ) + + if not result['files']: + # Check if this is a known limitation (no CAD support yet) + # Return "coming soon" message instead of error + return Response( + { + 'status': 'coming_soon', + 'message': '3D model generation is coming soon for this module', + 'files': {}, + 'hover_dict': {} + }, + status=200 + ) + + return Response({ + 'status': 'success', + 'files': result['files'], + 'hover_dict': result['hover_dict'], + 'message': 'CAD models generated successfully', + 'warnings': result['warnings'] + }, status=201) + + except Exception as e: + print(f"[SimpleConnectionViewSet] Error generating CAD: {e}") + import traceback + traceback.print_exc() + # For modules without CAD support, return "coming soon" instead of error + # Check if it's a CAD-related error (ValueError, AttributeError, etc.) + if isinstance(e, (ValueError, AttributeError, ImportError)): + return Response( + { + 'status': 'coming_soon', + 'message': '3D model generation is coming soon for this module', + 'files': {}, + 'hover_dict': {} + }, + status=200 + ) + # For other errors, return error response + return Response( + {'error': str(e), 'status': 'error'}, + status=500 + ) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/report/generate-initial') + def report_generate_initial(self, request, submodule_slug=None): + """ + POST /api/modules/simple-connection/{submodule_slug}/report/generate-initial/ + + Currently a placeholder: simple-connection modules do not yet have + desktop-style design report support wired into the backend. + """ + module_id = SIMPLE_CONNECTION_REPORT_MODULE_ID_MAP.get(submodule_slug) + if not module_id: + return Response( + { + "success": False, + "error": ( + f"Report generation is not yet implemented for simple-connection " + f"sub-module '{submodule_slug}'." + ), + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + input_values = request.data.get("input_values") or request.data.get("inputs") + if not input_values: + return Response( + {"success": False, "error": "input_values are required"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + mapped_data = { + "module_id": module_id, + "input_values": input_values, + "metadata": request.data.get("metadata"), + "design_status": request.data.get("design_status", True), + "logs": request.data.get("logs", []), + } + + if "sections" in request.data: + mapped_data["sections"] = request.data.get("sections") + if "customization" in request.data: + mapped_data["customization"] = request.data.get("customization") + + payload, status_code = generate_initial_report_core(mapped_data) + return Response(payload, status=status_code) diff --git a/backend/apps/modules/tension_member/__init__.py b/backend/apps/modules/tension_member/__init__.py new file mode 100644 index 000000000..aa6058c48 --- /dev/null +++ b/backend/apps/modules/tension_member/__init__.py @@ -0,0 +1,4 @@ +""" +Tension Member Module +Parent module for tension member design calculations +""" diff --git a/backend/apps/modules/tension_member/apps.py b/backend/apps/modules/tension_member/apps.py new file mode 100644 index 000000000..34a87793f --- /dev/null +++ b/backend/apps/modules/tension_member/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class TensionMemberConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.modules.tension_member' + diff --git a/backend/apps/modules/tension_member/registry.py b/backend/apps/modules/tension_member/registry.py new file mode 100644 index 000000000..81c65590c --- /dev/null +++ b/backend/apps/modules/tension_member/registry.py @@ -0,0 +1,18 @@ +""" +Tension Member Registry - Auto-discovers sub-modules +Inherits from BaseModuleRegistry (DRY principle) +""" +import os +from apps.core.registry import BaseModuleRegistry + + +class TensionMemberRegistry(BaseModuleRegistry): + """Registry for tension member sub-modules""" + pass + + +# Auto-discover sub-modules +_package_name = 'apps.modules.tension_member.submodules' +_package_path = os.path.join(os.path.dirname(__file__), 'submodules') +TensionMemberRegistry.auto_discover(_package_name, _package_path) + diff --git a/backend/apps/modules/tension_member/shared.py b/backend/apps/modules/tension_member/shared.py new file mode 100644 index 000000000..6b159562a --- /dev/null +++ b/backend/apps/modules/tension_member/shared.py @@ -0,0 +1,18 @@ +""" +Shared utilities for tension member modules +""" +from osdag_core.cad.common_logic import CommonDesignLogic +from OCC.Display.backend import * +from osdag_core.Common import * + + +def setup_for_cad(cdl: CommonDesignLogic, module_class): + """Sets up the CommonLogicObject before generating CAD""" + print('SETTING UP FOR CAD', module_class) + cdl.module_class = module_class # Set the module class in design logic object. + cdl.module_object = module_class # Set the module object (required by common_logic.py) + module_object = module_class + print(module_object.module) + if module_object.module == "Tension-Member-Bolted-Design" or module_object.module == "Tension-Member-Welded-Design": + cdl.TObj = cdl.createTensionCAD() + diff --git a/backend/apps/modules/tension_member/submodules/__init__.py b/backend/apps/modules/tension_member/submodules/__init__.py new file mode 100644 index 000000000..e6a890cfe --- /dev/null +++ b/backend/apps/modules/tension_member/submodules/__init__.py @@ -0,0 +1,4 @@ +""" +Tension Member Sub-modules +""" + diff --git a/backend/apps/modules/tension_member/submodules/bolted/__init__.py b/backend/apps/modules/tension_member/submodules/bolted/__init__.py new file mode 100644 index 000000000..431a14775 --- /dev/null +++ b/backend/apps/modules/tension_member/submodules/bolted/__init__.py @@ -0,0 +1,6 @@ +""" +Tension Member - Bolted Design Sub-module +""" +MODULE_ID = "Tension-Member-Bolted-Design" +from .service import BoltedTensionMemberService as Service + diff --git a/backend/apps/modules/tension_member/submodules/bolted/adapter.py b/backend/apps/modules/tension_member/submodules/bolted/adapter.py new file mode 100644 index 000000000..f170ceb4c --- /dev/null +++ b/backend/apps/modules/tension_member/submodules/bolted/adapter.py @@ -0,0 +1,341 @@ +""" +Tension Member - Bolted Design Adapter +Implements the business logic directly (not re-exporting from osdag_api) +""" +from osdag_core.Common import KEY_DISP_TENSION_BOLTED +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl +) +from apps.modules.tension_member import shared as tbm +from OCC.Core import BRepTools +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.design_type.tension_member.tension_bolted import Tension_bolted +import sys +import os +import typing +from typing import Dict, Any, List +import json +import traceback + +old_stdout = sys.stdout # Backup log +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + """Return all required input parameters for the module.""" + return [ + "Member.Profile", # KEY_SEC_PROFILE + "Member.Designation", # KEY_SECSIZE + "Material", # KEY_MATERIAL + "Member.Material", # KEY_SEC_MATERIAL + "Connector.Plate.Thickness_List", # KEY_PLATETHK + "Bolt.Diameter", # KEY_D + "Bolt.Grade", # KEY_GRD + "Bolt.Type", # KEY_TYP + "Bolt.Bolt_Hole_Type", # KEY_DP_BOLT_HOLE_TYPE + "Bolt.Slip_Factor", # KEY_DP_BOLT_SLIP_FACTOR + "Connector.Material", # KEY_CONNECTOR_MATERIAL + "Design.Design_Method", # KEY_DP_DESIGN_METHOD + "Detailing.Corrosive_Influences", # KEY_DP_DETAILING_CORROSIVE_INFLUENCES + "Detailing.Edge_type", # KEY_DP_DETAILING_EDGE_TYPE + "Detailing.Gap", # KEY_DP_DETAILING_GAP + "Load.Axial", # KEY_AXIAL + "Member.Length", # KEY_LENGTH + "Conn_Location", # KEY_LOCATION + "Module" # KEY_MODULE + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + required_keys = get_required_keys() + missing_keys = contains_keys(input_values, required_keys) + if missing_keys is not None: + raise MissingKeyError(missing_keys[0]) + + # Validate Bolt.Bolt_Hole_Type + if not isinstance(input_values["Bolt.Bolt_Hole_Type"], str): + raise InvalidInputTypeError("Bolt.Bolt_Hole_Type", "str") + + # Validate Bolt.Diameter + bolt_diameter = input_values["Bolt.Diameter"] + if (not isinstance(bolt_diameter, list) + or not validate_list_type(bolt_diameter, str) + or not custom_list_validation(bolt_diameter, int_able)): + raise InvalidInputTypeError("Bolt.Diameter", "non empty List[str] where all items can be converted to int") + + # Validate Bolt.Grade + bolt_grade = input_values["Bolt.Grade"] + if (not isinstance(bolt_grade, list) + or not validate_list_type(bolt_grade, str) + or not custom_list_validation(bolt_grade, float_able)): + raise InvalidInputTypeError("Bolt.Grade", "non empty List[str] where all items can be converted to float") + + # Validate Bolt.Slip_Factor + bolt_slipfactor = input_values["Bolt.Slip_Factor"] + if (not isinstance(bolt_slipfactor, str) + or not float_able(bolt_slipfactor)): + raise InvalidInputTypeError("Bolt.Slip_Factor", "str where str can be converted to float") + + # Validate Bolt.Type + if not isinstance(input_values["Bolt.Type"], str): + raise InvalidInputTypeError("Bolt.Type", "str") + + # Validate Connector.Material + if not isinstance(input_values["Connector.Material"], str): + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + if not isinstance(input_values["Design.Design_Method"], str): + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + raise InvalidInputTypeError("Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + if not isinstance(input_values["Detailing.Edge_type"], str): + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) + or not int_able(detailing_gap)): + raise InvalidInputTypeError("Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) + or not int_able(load_axial)): + raise InvalidInputTypeError("Load.Axial", "str where str can be converted to int") + + # Validate Material + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") + + # Validate Member.Profile + if not isinstance(input_values["Member.Profile"], str): + raise InvalidInputTypeError("Member.Profile", "str") + + # Validate Member.Designation + if not isinstance(input_values["Member.Designation"], str): + raise InvalidInputTypeError("Member.Designation", "str") + + # Validate Member.Length + if not isinstance(input_values["Member.Length"], str): + raise InvalidInputTypeError("Member.Length", "str") + + # Validate Conn_Location + if not isinstance(input_values["Conn_Location"], str): + raise InvalidInputTypeError("Conn_Location", "str") + + # Validate Module + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") + + # Validate Connector.Plate.Thickness_List + connector_plate_thicknesslist = input_values["Connector.Plate.Thickness_List"] + if (not isinstance(connector_plate_thicknesslist, list) + or not validate_list_type(connector_plate_thicknesslist, str) + or not custom_list_validation(connector_plate_thicknesslist, int_able)): + raise InvalidInputTypeError("Connector.Plate.Thickness_List", "List[str] where all items can be converted to int") + + +def create_module() -> Tension_bolted: + """Create an instance of the Tension_bolted module design class and set it up for use""" + module = Tension_bolted() + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> Tension_bolted: + """Create an instance of the Tension_bolted module design class from input values.""" + module = create_module() + # Plate.Thickness expects a list, take the first value if present, else "" + if isinstance(input_values.get("Connector.Plate.Thickness_List", None), list) and input_values["Connector.Plate.Thickness_List"]: + input_values["Plate.Thickness"] = input_values["Connector.Plate.Thickness_List"][0] + else: + input_values["Plate.Thickness"] = "" + + module.set_input_values(input_values) + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate, format and return the output values from the given input values.""" + output = {} + module = create_from_input(input_values) + raw_output_text = module.output_values(True) + raw_output_spacing = module.spacing(True) + logs = module.logs + raw_output = raw_output_spacing + raw_output_text + for param in raw_output: + if param[2] == "TextBox": + key = param[0] + label = param[1] + value = param[3] + output[key] = { + "key": key, + "label": label, + "val": value + } + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + if section not in ("Model", "Member", "Plate", "Endplate"): + raise InvalidInputTypeError("section", "'Model', 'Member', 'Plate', 'Endplate'") + + module = create_from_input(input_values) + + # Object that will create the CAD model. + try: + cld = CommonDesignLogic(None, None, '', KEY_DISP_TENSION_BOLTED, module.mainmodule) + except Exception as e : + print('error in cld e : ' , e) + raise + + try: + # Setup the calculations object for generating CAD model. + tbm.setup_for_cad(cld, module) + except Exception as e : + traceback.print_exc() + print('Error in setting up cad e : ' , e) + raise + + # The section of the module that will be generated. + cld.component = section + + # When section == "Model", also ensure per-part shapes exist and prepare a compound + part_names = ["Member", "Plate", "Endplate"] + part_files = {} + compound_model = None + + try: + if section == "Model": + # Build compound by adding each part shape without fusing + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + + for part in part_names: + try: + # Generate shape for this part + cld.component = part + part_shape = cld.create2Dcad() + if part_shape is None: + continue + + # Add to compound + builder.Add(compound, part_shape) + + # Ensure per-part BREP file exists (write or overwrite) + part_file_name = f"{session}_{part}.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + BRepTools.breptools.Write(part_shape, part_file_path_rel, Message_ProgressRange()) + part_files[part] = part_file_path_rel + # Also write STL for this part + try: + part_stl_rel = part_file_path_rel.replace(".brep", ".stl") + write_stl(part_shape, os.path.join(os.getcwd(), part_stl_rel)) + except Exception as stle: + print(f"Failed to write STL for part {part}: {stle}") + except Exception as e: + print(f"Failed to build/write part {part}: {e}") + + # Reset component to Model and set compound as the model to write + cld.component = section + compound_model = compound + # Generate model for non-Model sections (or fallback) + if compound_model is not None: + model = compound_model + else: + model = cld.create2Dcad() + except Exception as e : + print("Error in cld.create2Dcad() e : " , e) + raise + + # check if the cad_models folder exists or not + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print("path does not exists cad_models , creating one") + os.makedirs(cad_models_path, exist_ok=True) + + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + + try : + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) + + # If it's "Model" section, write a manifest referencing per-part breps and save extra formats + if section == "Model": + try: + manifest = { + "session": session, + "mergedBrep": file_path, + "parts": [ + {"name": name, "brepPath": part_files.get(name)} for name in part_names if part_files.get(name) + ] + } + # add stlPath for parts + for entry in manifest["parts"]: + if entry.get("brepPath"): + entry["stlPath"] = entry["brepPath"].replace(".brep", ".stl") + manifest_path = file_path.replace(".brep", ".parts.json") + full_manifest_path = os.path.join(os.getcwd(), manifest_path) + with open(full_manifest_path, "w", encoding="utf-8") as mf: + json.dump(manifest, mf) + print(f"Parts manifest saved at {full_manifest_path}") + except Exception as me: + print(f"Warning: Failed to write manifest: {me}") + + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + # Write merged STL for Model + try: + merged_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), merged_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), merged_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save merged STL: {stle}") + except Exception as e : + print('Writing to BREP file failed e : ' , e) + raise + + # For non-Model sections, export single STL next to BREP + if section != "Model": + try: + single_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), single_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save STL for {section}: {stle}") + + return file_path diff --git a/backend/apps/modules/tension_member/submodules/bolted/service.py b/backend/apps/modules/tension_member/submodules/bolted/service.py new file mode 100644 index 000000000..bb7c434ca --- /dev/null +++ b/backend/apps/modules/tension_member/submodules/bolted/service.py @@ -0,0 +1,94 @@ +""" +Tension Member - Bolted Service - Business logic layer +Bridges between API and osdag_core +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class BoltedTensionMemberService: + """Service class for Tension-Member-Bolted-Design module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """ + Run design calculation and return results. + + Args: + inputs: Dictionary of input parameters + request: Optional Django request object (for future use) + project_id: Optional project ID (for future use) + user_email: Optional user email (for future use) + + Returns: + Dictionary with 'data' (results) and 'logs' (calculation logs) + """ + print("=" * 60) + print("BoltedTensionMemberService.calculate() called") + print("=" * 60) + print(f"Inputs received: {list(inputs.keys())[:10]}...") # Print first 10 keys + + try: + # Validate inputs + print("\n[1/3] Validating inputs...") + validate_input(inputs) + print("✅ Input validation passed") + + # Generate formatted output (this handles module creation and calculation) + print("\n[2/3] Generating output (creates module and runs calculation)...") + output, logs = generate_output(inputs) + print(f"✅ Output generated: {len(output)} output parameters") + print(f"✅ Logs retrieved: {len(logs) if logs else 0} log entries") + + print("\n[3/3] Preparing response...") + result = { + 'data': output, + 'logs': logs or [], # Ensure logs is always a list + 'success': True + } + print("✅ Response prepared successfully") + print("=" * 60) + + return result + + except Exception as e: + print("\n" + "=" * 60) + print("ERROR in BoltedTensionMemberService.calculate()") + print("=" * 60) + print(f"Exception type: {type(e).__name__}") + print(f"Exception message: {str(e)}") + + # Safely extract error message + error_msg = str(e) + if hasattr(e, 'error') and e.error is not None: + error_msg = str(e.error) + elif hasattr(e, 'args') and len(e.args) > 0: + error_msg = str(e.args[0]) + + print(f"Final error message: {error_msg}") + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60) + + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': error_msg + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate ('Model', 'Member', 'Plate', 'Endplate') + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/tension_member/submodules/welded/__init__.py b/backend/apps/modules/tension_member/submodules/welded/__init__.py new file mode 100644 index 000000000..827500599 --- /dev/null +++ b/backend/apps/modules/tension_member/submodules/welded/__init__.py @@ -0,0 +1,6 @@ +""" +Tension Member - Welded Design Sub-module +""" +MODULE_ID = "Tension-Member-Welded-Design" +from .service import WeldedTensionMemberService as Service + diff --git a/backend/apps/modules/tension_member/submodules/welded/adapter.py b/backend/apps/modules/tension_member/submodules/welded/adapter.py new file mode 100644 index 000000000..4ff6b5b90 --- /dev/null +++ b/backend/apps/modules/tension_member/submodules/welded/adapter.py @@ -0,0 +1,311 @@ +""" +Tension Member - Welded Design Adapter +Implements the business logic directly (not re-exporting from osdag_api) +""" +from osdag_core.Common import KEY_DISP_TENSION_WELDED +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type, + write_stl +) +from apps.modules.tension_member import shared as tbm +from OCC.Core import BRepTools +from OCC.Core.Message import Message_ProgressRange +from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs +from OCC.Core.IGESControl import IGESControl_Writer +from OCC.Core.TopoDS import TopoDS_Compound +from OCC.Core.BRep import BRep_Builder +from osdag_core.cad.common_logic import CommonDesignLogic +from osdag_core.design_type.tension_member.tension_welded import Tension_welded +import sys +import os +import typing +from typing import Dict, Any, List +import json +import traceback + +old_stdout = sys.stdout # Backup logs +sys.stdout = open(os.devnull, "w") # redirect stdout +sys.stdout = old_stdout # Reset log + + +def get_required_keys() -> List[str]: + """Return all required input parameters for the module.""" + return [ + "Member.Profile", # KEY_SEC_PROFILE + "Member.Designation", # KEY_SECSIZE + "Material", # KEY_MATERIAL + "Member.Material", # KEY_SEC_MATERIAL + "Connector.Plate.Thickness_List", # KEY_PLATETHK + "Connector.Material", # KEY_CONNECTOR_MATERIAL + "Design.Design_Method", # KEY_DP_DESIGN_METHOD + "Detailing.Corrosive_Influences", # KEY_DP_DETAILING_CORROSIVE_INFLUENCES + "Detailing.Edge_type", # KEY_DP_DETAILING_EDGE_TYPE + "Detailing.Gap", # KEY_DP_DETAILING_GAP + "Load.Axial", # KEY_AXIAL + "Member.Length", # KEY_LENGTH + "Conn_Location", # KEY_LOCATION + "Module" # KEY_MODULE + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate type for all values in design dict. Raise error when invalid""" + required_keys = get_required_keys() + missing_keys = contains_keys(input_values, required_keys) + if missing_keys is not None: + raise MissingKeyError(missing_keys[0]) + + # Validate Connector.Material + if not isinstance(input_values["Connector.Material"], str): + raise InvalidInputTypeError("Connector.Material", "str") + + # Validate Design.Design_Method + if not isinstance(input_values["Design.Design_Method"], str): + raise InvalidInputTypeError("Design.Design_Method", "str") + + # Validate Detailing.Corrosive_Influences + if not is_yes_or_no(input_values["Detailing.Corrosive_Influences"]): + raise InvalidInputTypeError("Detailing.Corrosive_Influences", "'Yes' or 'No'") + + # Validate Detailing.Edge_type + if not isinstance(input_values["Detailing.Edge_type"], str): + raise InvalidInputTypeError("Detailing.Edge_type", "str") + + # Validate Detailing.Gap + detailing_gap = input_values["Detailing.Gap"] + if (not isinstance(detailing_gap, str) + or not int_able(detailing_gap)): + raise InvalidInputTypeError("Detailing.Gap", "str where str can be converted to int") + + # Validate Load.Axial + load_axial = input_values["Load.Axial"] + if (not isinstance(load_axial, str) + or not int_able(load_axial)): + raise InvalidInputTypeError("Load.Axial", "str where str can be converted to int") + + # Validate Material + if not isinstance(input_values["Material"], str): + raise InvalidInputTypeError("Material", "str") + + # Validate Member.Profile + if not isinstance(input_values["Member.Profile"], str): + raise InvalidInputTypeError("Member.Profile", "str") + + # Validate Member.Designation + if not isinstance(input_values["Member.Designation"], str): + raise InvalidInputTypeError("Member.Designation", "str") + + # Validate Member.Length + if not isinstance(input_values["Member.Length"], str): + raise InvalidInputTypeError("Member.Length", "str") + + # Validate Conn_Location + if not isinstance(input_values["Conn_Location"], str): + raise InvalidInputTypeError("Conn_Location", "str") + + # Validate Module + if not isinstance(input_values["Module"], str): + raise InvalidInputTypeError("Module", "str") + + # Validate Connector.Plate.Thickness_List + connector_plate_thicknesslist = input_values["Connector.Plate.Thickness_List"] + if (not isinstance(connector_plate_thicknesslist, list) + or not validate_list_type(connector_plate_thicknesslist, str) + or not custom_list_validation(connector_plate_thicknesslist, int_able)): + raise InvalidInputTypeError("Connector.Plate.Thickness_List", "List[str] where all items can be converted to int") + + +def create_module() -> Tension_welded: + """Create an instance of the Tension_welded module design class and set it up for use""" + module = Tension_welded() + module.set_osdaglogger(None, id="web") + return module + + +def create_from_input(input_values: Dict[str, Any]) -> Tension_welded: + """Create an instance of the Tension_welded module design class from input values.""" + module = create_module() + # Plate.Thickness expects a list, take the first value if present, else "" + if isinstance(input_values.get("Connector.Plate.Thickness_List", None), list) and input_values["Connector.Plate.Thickness_List"]: + input_values["Plate.Thickness"] = input_values["Connector.Plate.Thickness_List"][0] + else: + input_values["Plate.Thickness"] = "" + + # Set weld defaults + input_values["Weld.Material_Grade_OverWrite"] = "410" + input_values["Weld.Fab"] = "Shop Weld" + module.set_input_values(input_values) + return module + + +def generate_output(input_values: Dict[str, Any]) -> Dict[str, Any]: + """Generate, format and return the output values from the given input values.""" + output = {} + module = create_from_input(input_values) + raw_output_text = module.output_values(True) + raw_output_spacing = module.spacing(True) + logs = module.logs + raw_output = raw_output_spacing + raw_output_text + for param in raw_output: + if param[2] == "TextBox": + key = param[0] + label = param[1] + value = param[3] + output[key] = { + "key": key, + "label": label, + "val": value + } + return output, logs + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """Generate the CAD model from input values as a BREP file. Return file path.""" + if section not in ("Model", "Member", "Plate", "Endplate"): + raise InvalidInputTypeError("section", "'Model', 'Member', 'Plate', 'Endplate'") + + module = create_from_input(input_values) + + # Object that will create the CAD model. + try: + cld = CommonDesignLogic(None, None, '', KEY_DISP_TENSION_WELDED, module.mainmodule) + except Exception as e : + print('error in cld e : ' , e) + raise + + try: + # Setup the calculations object for generating CAD model. + tbm.setup_for_cad(cld, module) + except Exception as e : + traceback.print_exc() + print('Error in setting up cad e : ' , e) + raise + + # The section of the module that will be generated. + cld.component = section + + # When section == "Model", also ensure per-part shapes exist and prepare a compound + part_names = ["Member", "Plate", "Endplate"] + part_files = {} + compound_model = None + + try: + if section == "Model": + # Build compound by adding each part shape without fusing + builder = BRep_Builder() + compound = TopoDS_Compound() + builder.MakeCompound(compound) + + for part in part_names: + try: + # Generate shape for this part + cld.component = part + part_shape = cld.create2Dcad() + if part_shape is None: + continue + + # Add to compound + builder.Add(compound, part_shape) + + # Ensure per-part BREP file exists (write or overwrite) + part_file_name = f"{session}_{part}.brep" + part_file_path_rel = os.path.join("file_storage", "cad_models", part_file_name) + BRepTools.breptools.Write(part_shape, part_file_path_rel, Message_ProgressRange()) + part_files[part] = part_file_path_rel + # Also write STL for this part + try: + part_stl_rel = part_file_path_rel.replace(".brep", ".stl") + write_stl(part_shape, os.path.join(os.getcwd(), part_stl_rel)) + except Exception as stle: + print(f"Failed to write STL for part {part}: {stle}") + except Exception as e: + print(f"Failed to build/write part {part}: {e}") + + # Reset component to Model and set compound as the model to write + cld.component = section + compound_model = compound + # Generate model for non-Model sections (or fallback) + if compound_model is not None: + model = compound_model + else: + model = cld.create2Dcad() + except Exception as e : + print("Error in cld.create2Dcad() e : " , e) + raise + + # check if the cad_models folder exists or not + cad_models_path = os.path.join(os.getcwd(), "file_storage", "cad_models") + if not os.path.exists(cad_models_path): + print("path does not exists cad_models , creating one") + os.makedirs(cad_models_path, exist_ok=True) + + file_name = session + "_" + section + ".brep" + file_path = "file_storage/cad_models/" + file_name + + try : + BRepTools.breptools.Write(model, file_path, Message_ProgressRange()) + + # If it's "Model" section, write a manifest referencing per-part breps and save extra formats + if section == "Model": + try: + manifest = { + "session": session, + "mergedBrep": file_path, + "parts": [ + {"name": name, "brepPath": part_files.get(name)} for name in part_names if part_files.get(name) + ] + } + # add stlPath for parts + for entry in manifest["parts"]: + if entry.get("brepPath"): + entry["stlPath"] = entry["brepPath"].replace(".brep", ".stl") + manifest_path = file_path.replace(".brep", ".parts.json") + full_manifest_path = os.path.join(os.getcwd(), manifest_path) + with open(full_manifest_path, "w", encoding="utf-8") as mf: + json.dump(manifest, mf) + print(f"Parts manifest saved at {full_manifest_path}") + except Exception as me: + print(f"Warning: Failed to write manifest: {me}") + + # Save STEP + step_writer = STEPControl_Writer() + step_writer.Transfer(model, STEPControl_AsIs) + step_file_path = file_path.replace(".brep", ".step") + full_step_file_path = os.path.join(os.getcwd(), step_file_path) + if step_writer.Write(full_step_file_path) == 1: + print(f"STEP file saved at {full_step_file_path}") + else: + print("Warning: Failed to save STEP file!") + + # Save IGES + iges_writer = IGESControl_Writer() + iges_writer.AddShape(model) + iges_file_path = file_path.replace(".brep", ".iges") + full_iges_file_path = os.path.join(os.getcwd(), iges_file_path) + if iges_writer.Write(full_iges_file_path) == 1: + print(f"IGES file saved at {full_iges_file_path}") + else: + print("Warning: Failed to save IGES file!") + # Write merged STL for Model + try: + merged_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), merged_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), merged_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save merged STL: {stle}") + except Exception as e : + print('Writing to BREP file failed e : ' , e) + raise + + # For non-Model sections, export single STL next to BREP + if section != "Model": + try: + single_stl_rel = file_path.replace(".brep", ".stl") + write_stl(model, os.path.join(os.getcwd(), single_stl_rel)) + print(f"STL file saved at {os.path.join(os.getcwd(), single_stl_rel)}") + except Exception as stle: + print(f"Warning: Failed to save STL for {section}: {stle}") + + return file_path diff --git a/backend/apps/modules/tension_member/submodules/welded/service.py b/backend/apps/modules/tension_member/submodules/welded/service.py new file mode 100644 index 000000000..085534d78 --- /dev/null +++ b/backend/apps/modules/tension_member/submodules/welded/service.py @@ -0,0 +1,94 @@ +""" +Tension Member - Welded Service - Business logic layer +Bridges between API and osdag_core +""" +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class WeldedTensionMemberService: + """Service class for Tension-Member-Welded-Design module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """ + Run design calculation and return results. + + Args: + inputs: Dictionary of input parameters + request: Optional Django request object (for future use) + project_id: Optional project ID (for future use) + user_email: Optional user email (for future use) + + Returns: + Dictionary with 'data' (results) and 'logs' (calculation logs) + """ + print("=" * 60) + print("WeldedTensionMemberService.calculate() called") + print("=" * 60) + print(f"Inputs received: {list(inputs.keys())[:10]}...") # Print first 10 keys + + try: + # Validate inputs + print("\n[1/3] Validating inputs...") + validate_input(inputs) + print("✅ Input validation passed") + + # Generate formatted output (this handles module creation and calculation) + print("\n[2/3] Generating output (creates module and runs calculation)...") + output, logs = generate_output(inputs) + print(f"✅ Output generated: {len(output)} output parameters") + print(f"✅ Logs retrieved: {len(logs) if logs else 0} log entries") + + print("\n[3/3] Preparing response...") + result = { + 'data': output, + 'logs': logs or [], # Ensure logs is always a list + 'success': True + } + print("✅ Response prepared successfully") + print("=" * 60) + + return result + + except Exception as e: + print("\n" + "=" * 60) + print("ERROR in WeldedTensionMemberService.calculate()") + print("=" * 60) + print(f"Exception type: {type(e).__name__}") + print(f"Exception message: {str(e)}") + + # Safely extract error message + error_msg = str(e) + if hasattr(e, 'error') and e.error is not None: + error_msg = str(e.error) + elif hasattr(e, 'args') and len(e.args) > 0: + error_msg = str(e.args[0]) + + print(f"Final error message: {error_msg}") + print("\nFull traceback:") + traceback.print_exc() + print("=" * 60) + + return { + 'data': {}, + 'logs': [], + 'success': False, + 'error': error_msg + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model and return file path. + + Args: + inputs: Dictionary of input parameters + section: Section to generate ('Model', 'Member', 'Plate', 'Endplate') + session: Session identifier for file naming + + Returns: + File path to the generated CAD model + """ + return create_cad_model(inputs, section, session) + diff --git a/backend/apps/modules/tension_member/urls.py b/backend/apps/modules/tension_member/urls.py new file mode 100644 index 000000000..0f7eecd3c --- /dev/null +++ b/backend/apps/modules/tension_member/urls.py @@ -0,0 +1,12 @@ +""" +Tension Member URLs +""" +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import TensionMemberViewSet + +router = DefaultRouter() +router.register(r'', TensionMemberViewSet, basename='tension-member') + +urlpatterns = router.urls + diff --git a/backend/apps/modules/tension_member/views.py b/backend/apps/modules/tension_member/views.py new file mode 100644 index 000000000..7fb62745d --- /dev/null +++ b/backend/apps/modules/tension_member/views.py @@ -0,0 +1,339 @@ +""" +Tension Member ViewSet - Routes to sub-module services +Uses URL slug (not POST body) to find the correct service +Handles guest mode and optional project_id saving +""" +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from .registry import TensionMemberRegistry +from apps.core.utils.module_helpers import handle_design_request +from apps.core.utils.cad_helpers import generate_cad_models, get_default_sections +from apps.core.models import Material, CustomMaterials, Bolt, Angles, Channels +from apps.core.api.design.report_customization_api import generate_initial_report_core + + +# Mapping from tension-member slug to legacy report module_id +TENSION_REPORT_MODULE_ID_MAP = { + "bolted": "Tension-Member-Bolted-Design", + "welded": "Tension-Member-Welded-Design", +} + + +class TensionMemberViewSet(viewsets.ViewSet): + """ + Generic ViewSet that routes to specific sub-module services based on URL slug. + Supports guest mode and optional project_id saving for authenticated users. + """ + permission_classes = [AllowAny] # Allow both authenticated and guest users + + @staticmethod + def _normalize_slug(raw_slug: str) -> str: + """ + Accept both URL slugs and MODULE_ID values from legacy/frontend calls. + Example inputs: + - 'bolted', 'welded' (preferred) + - 'Tension-Member-Bolted-Design', 'Tension-Member-Welded-Design' (legacy) + """ + if not raw_slug: + return raw_slug + slug_lower = raw_slug.lower() + module_id_map = { + 'tension-member-bolted-design': 'bolted', + 'tension-member-welded-design': 'welded', + } + return module_id_map.get(slug_lower, raw_slug) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/design') + def design(self, request, submodule_slug=None): + """ + POST /api/modules/tension-member/{submodule_slug}/design/ + + Request body: + { + "inputs": {...}, # Design input parameters + "project_id": 123 # Optional: Save results to project if user is authenticated + } + + Example: POST /api/modules/tension-member/bolted/design/ + + Guest Mode: + - Can calculate designs + - Cannot save to projects (project_id is ignored) + + Authenticated Users: + - Can calculate designs + - Can save to projects if project_id is provided + """ + # Normalize slug (supports MODULE_ID inputs) + normalized_slug = self._normalize_slug(submodule_slug) + + # Use URL slug to find service (not POST body) + ServiceClass = TensionMemberRegistry.get_service_by_slug(normalized_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {normalized_slug} not found'}, + status=404 + ) + + # Extract inputs and optional project_id + inputs = request.data.get('inputs', request.data) # Support both formats + project_id = request.data.get('project_id') + + # Handle authentication and project saving (shared logic) + context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug=normalized_slug, + module_name='tension-member' + ) + + try: + # Call the service with request context + result = ServiceClass.calculate( + inputs=inputs, + request=request, + project_id=project_id if not context['is_guest'] else None, + user_email=context['user_email'] + ) + + # Add project saving result to response + if context['project_result']: + result['project_saved'] = context['project_result']['saved'] + if context['project_result'].get('project_id'): + result['project_id'] = context['project_result']['project_id'] + if context['project_result'].get('error'): + result['project_error'] = context['project_result']['error'] + + return Response(result, status=200) + except Exception as e: + return Response( + {'error': str(e), 'success': False}, + status=400 + ) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/report/generate-initial') + def report_generate_initial(self, request, submodule_slug=None): + """ + POST /api/modules/tension-member/{submodule_slug}/report/generate-initial/ + + Request body: + { + "metadata": {...}, + "input_values": {...}, # Or "inputs": {...} + "design_status": boolean, + "logs": [...], + "sections": [...], # Optional + "customization": {...} # Optional + } + """ + normalized_slug = self._normalize_slug(submodule_slug) + module_id = TENSION_REPORT_MODULE_ID_MAP.get(normalized_slug) + if not module_id: + return Response( + { + "success": False, + "error": f"Report generation is not supported for tension-member sub-module '{normalized_slug}'", + }, + status=status.HTTP_400_BAD_REQUEST, + ) + + input_values = request.data.get("input_values") or request.data.get("inputs") + if not input_values: + return Response( + {"success": False, "error": "input_values are required"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + mapped_data = { + "module_id": module_id, + "input_values": input_values, + "metadata": request.data.get("metadata"), + "design_status": request.data.get("design_status", True), + "logs": request.data.get("logs", []), + } + + if "sections" in request.data: + mapped_data["sections"] = request.data.get("sections") + if "customization" in request.data: + mapped_data["customization"] = request.data.get("customization") + + payload, status_code = generate_initial_report_core(mapped_data) + return Response(payload, status=status_code) + + @action(detail=False, methods=['get'], url_path='(?P[^/.]+)/options') + def options(self, request, submodule_slug=None): + """ + GET /api/modules/tension-member/{submodule_slug}/options/ + + Returns input options for the sub-module (e.g., section lists, materials) + """ + email = request.query_params.get("email") + slug = self._normalize_slug(submodule_slug) + + # Shared helpers + def material_list(): + mats = list(Material.objects.all().values()) + if email: + mats += list(CustomMaterials.objects.filter(email=email).values()) + mats.append({"id": -1, "Grade": "Custom"}) + return mats + + def bolt_diameters(): + lst = list(Bolt.objects.values_list('Bolt_diameter', flat=True)) + lst.sort() + return [str(x) for x in lst] + + property_classes = ['3.6', '4.6', '4.8', '5.6', '5.8', '6.8', '8.8', '9.8', '10.9', '12.9'] + thickness_list = [ + '8', '10', '12', '14', '16', '18', '20', '22', '25', '28', '32', '36', '40', '45', '50', + '56', '63', '75', '80', '90', '100', '110', '120' + ] + section_profiles = ["Angles", "Back to Back Angles", "Star Angles", "Channels"] + bolt_hole_type_list = ["Standard", "Oversized", "Short Slotted", "Long Slotted"] + bolt_type_list = ["Bearing Bolt", "Friction Grip Bolt"] + bolt_slip_factor_list = ["0.3", "0.5"] + design_method_list = ["Limit State Design", "Working Stress Design"] + edge_type_list = ["Rolled, machine-flame cut, sawn and planed"] + corrosive_influences_list = ["Yes", "No"] + + try: + if slug == 'bolted': + data = { + 'materialList': material_list(), + 'connectorMaterialList': material_list(), + 'sectionProfileList': section_profiles, + 'angleList': list(Angles.objects.values_list('Designation', flat=True)), + 'channelList': list(Channels.objects.values_list('Designation', flat=True)), + 'boltDiameterList': bolt_diameters(), + 'propertyClassList': property_classes, + 'thicknessList': thickness_list, + 'boltHoleTypeList': bolt_hole_type_list, + 'boltTypeList': bolt_type_list, + 'boltSlipFactorList': bolt_slip_factor_list, + 'designMethodList': design_method_list, + 'edgeTypeList': edge_type_list, + 'corrosiveInfluencesList': corrosive_influences_list, + } + return Response(data, status=status.HTTP_200_OK) + + if slug == 'welded': + data = { + 'materialList': material_list(), + 'connectorMaterialList': material_list(), + 'sectionProfileList': section_profiles, + 'angleList': list(Angles.objects.values_list('Designation', flat=True)), + 'channelList': list(Channels.objects.values_list('Designation', flat=True)), + 'thicknessList': thickness_list, + 'designMethodList': design_method_list, + 'edgeTypeList': edge_type_list, + 'corrosiveInfluencesList': corrosive_influences_list, + } + return Response(data, status=status.HTTP_200_OK) + + return Response({'error': f'Sub-module {slug} not found'}, status=status.HTTP_404_NOT_FOUND) + except Exception as exc: + return Response({'error': str(exc)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/cad') + def cad(self, request, submodule_slug=None): + """ + POST /api/modules/tension-member/{submodule_slug}/cad/ + + Request body: + { + "inputs": {...}, # Design input parameters + "sections": ["Model", "Member", ...] # Optional: specific sections to generate + } + + Returns: + { + "status": "success", + "files": {section: base64_data, ...}, + "hover_dict": {...}, + "warnings": [...] + } + """ + # Normalize slug + normalized_slug = self._normalize_slug(submodule_slug) + + # Get service from registry + ServiceClass = TensionMemberRegistry.get_service_by_slug(normalized_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {normalized_slug} not found'}, + status=404 + ) + + # Extract inputs + inputs = request.data.get('inputs', request.data) + + if not inputs: + return Response( + {'error': 'inputs are required'}, + status=400 + ) + + # Get sections from request or use defaults + sections = request.data.get('sections') + if not sections: + sections = get_default_sections('tension-member', normalized_slug) + + if not sections: + return Response( + {'error': f'No sections defined for {normalized_slug}'}, + status=400 + ) + + try: + # Import adapter to get create_from_input function for hover_dict + create_from_input_func = None + try: + if normalized_slug == 'bolted': + from .submodules.bolted.adapter import create_from_input + create_from_input_func = create_from_input + elif normalized_slug == 'welded': + from .submodules.welded.adapter import create_from_input + create_from_input_func = create_from_input + except ImportError as e: + print(f"[TensionMemberViewSet] Could not import create_from_input for {normalized_slug}: {e}") + + # Generate CAD models + result = generate_cad_models( + service_class=ServiceClass, + inputs=inputs, + sections=sections, + create_from_input_func=create_from_input_func + ) + + if not result['files']: + return Response( + { + 'status': 'error', + 'message': 'No CAD models were generated', + 'errors': result['warnings'] + }, + status=422 + ) + + return Response({ + 'status': 'success', + 'files': result['files'], + 'hover_dict': result['hover_dict'], + 'message': 'CAD models generated successfully', + 'warnings': result['warnings'] + }, status=201) + + except Exception as e: + print(f"[TensionMemberViewSet] Error generating CAD: {e}") + import traceback + traceback.print_exc() + return Response( + {'error': str(e), 'status': 'error'}, + status=500 + ) + diff --git a/backend/apps/modules/urls.py b/backend/apps/modules/urls.py new file mode 100644 index 000000000..835301821 --- /dev/null +++ b/backend/apps/modules/urls.py @@ -0,0 +1,32 @@ +""" +Modules URL Aggregator +Aggregates all parent module URLs into a single include point +""" +from django.urls import path, include + +urlpatterns = [ + # Shear Connection module + path('shear-connection/', include('apps.modules.shear_connection.urls')), + + # Moment Connection module + path('moment-connection/', include('apps.modules.moment_connection.urls')), + + # Simple Connection module + path('simple-connection/', include('apps.modules.simple_connection.urls')), + + # Tension Member module + path('tension-member/', include('apps.modules.tension_member.urls')), + + # Flexure Member module + path('flexure-member/', include('apps.modules.flexure_member.urls')), + + # Compression Member module (using MODULE_ID format for frontend compatibility) + path('Compression-Member-Design/', include('apps.modules.compression_member.urls')), + + # Compression Member module (lowercase slug for new submodule routing) + path('compression-member/', include('apps.modules.compression_member.urls')), + + # Base Plate + path('base-plate/', include('apps.modules.base_plate.urls')), +] + diff --git a/backend/config/__init__.py b/backend/config/__init__.py new file mode 100644 index 000000000..92b625607 --- /dev/null +++ b/backend/config/__init__.py @@ -0,0 +1,2 @@ +# Config package (Django project settings) + diff --git a/backend/config/asgi.py b/backend/config/asgi.py new file mode 100644 index 000000000..b9ea4e298 --- /dev/null +++ b/backend/config/asgi.py @@ -0,0 +1,17 @@ +""" +ASGI config for osdag_on_web project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + +application = get_asgi_application() + diff --git a/backend/config/mailing.py b/backend/config/mailing.py new file mode 100644 index 000000000..dbdf6fd3b --- /dev/null +++ b/backend/config/mailing.py @@ -0,0 +1,48 @@ +import smtplib +import getpass + +# import variables from utils.py +from config import utils + +def send_mail(email , OTP=123) : + print('inside send email') + # OTP = 123 by default + HOST = utils.HOST + PORT = utils.PORT + print('host : ' ,HOST) + print('PORT ; ' ,PORT) + + FROM_EMAIL = utils.FROM_EMAIL + TO_EMAIL = str(email) + PASSWORD = utils.PASSWORD + print('TO email : ', TO_EMAIL) + print('password : ' , PASSWORD) + print('FROMT_EMAIL : ' ,FROM_EMAIL) + MESSAGE = f"""Subject: OTP for Email verification + + Hi, + + Your OTP for Email verification for Osdag on Cloud application is : {OTP} + + Thanks, + Osdag on Cloud Developer""" + print('MESSAGE : ' , MESSAGE) + print('sending...') + smtp = smtplib.SMTP(HOST , PORT) + + status_code, response = smtp.ehlo() + print(f"[*] Echoing the server : {status_code} {response}") + + status_code, response = smtp.starttls() + print(f"[*] Starting the TLS : {status_code} {response}") + + status_code, response = smtp.login(FROM_EMAIL , PASSWORD) + print(f"[*] Logging in : {status_code} {response}") + + try : + smtp.sendmail(FROM_EMAIL , TO_EMAIL , MESSAGE) + print('sent...') + except Exception as e : + print('An exception has occured while sending the mail : ' , e) + smtp.quit() + diff --git a/backend/config/postgres_credentials.py b/backend/config/postgres_credentials.py new file mode 100644 index 000000000..e988693b0 --- /dev/null +++ b/backend/config/postgres_credentials.py @@ -0,0 +1,21 @@ +######################################################### +# Author : Atharva Pingale ( FOSSEE Summer Fellow '23 ) # +######################################################### + + +def get_username() : + return "osdagdeveloper" + +def get_password() : + return "password" + +def get_host() : + return 'localhost' + + +def get_port() : + return '5432' + +def get_database_name() : + return 'postgres_Intg_osdag' + diff --git a/backend/config/secret_key.py b/backend/config/secret_key.py new file mode 100644 index 000000000..ca94cdd1c --- /dev/null +++ b/backend/config/secret_key.py @@ -0,0 +1,3 @@ +def get_secret_key(): + return 'django-insecure-3hy*!rp172se!ar4d%&e0!hi$937wm96-r=1$z*08mwj7gsz9d' + diff --git a/backend/config/settings.py b/backend/config/settings.py new file mode 100644 index 000000000..2a49bec11 --- /dev/null +++ b/backend/config/settings.py @@ -0,0 +1,201 @@ +""" +Django settings for osdag_web project. + +Generated by 'django-admin startproject' using Django 3.2.15. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.2/ref/settings/ +""" + +from pathlib import Path +from config.secret_key import get_secret_key +from config.postgres_credentials import get_database_name, get_host, get_password, get_port, get_username +import os +import sys + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent.parent + +# Add project root to Python path for osdag_core imports +# This allows: from osdag_core.design_type.connection.fin_plate_connection import FinPlateConnection +if str(BASE_DIR) not in sys.path: + sys.path.insert(0, str(BASE_DIR)) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = get_secret_key() +DATABASE_NAME = get_database_name() +USER = get_username() +PASSWORD = get_password() +PORT = get_port() +HOST = get_host() + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + + +# SECURITY WARNING: DEV ONLY - Never use ['*'] in production! +ALLOWED_HOSTS = ['*'] + +# CORS Configuration (DEV ONLY - Allow all origins for LAN testing) +CORS_ALLOWED_ORIGINS = [ + 'http://localhost:5173', + 'http://127.0.0.1:5173', + 'http://192.168.1.9:5173' # your local network ip +] +CORS_ALLOW_CREDENTIALS = True + +CORS_ALLOW_METHODS = ['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE'] + +CORS_ALLOW_HEADERS = ["accept", + "authorization", + "content-type", + "user-agent", + "x-csrftoken", + "x-requested-with", + "x-user-email", + 'access-control-allow-origin', + 'Cache-Control', + 'Pragma', + ] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'apps.core.apps.OsdagConfig', # New core app (replaces osdag) + 'apps.modules.shear_connection', # Shear connection parent module + 'apps.modules.moment_connection', # Moment connection parent module + 'apps.modules.tension_member', # Tension member parent module + 'apps.modules.flexure_member', # Flexure member parent module + # cors headers + 'corsheaders', + + # DRF + 'rest_framework', +] + +# Middleware order is CRITICAL - CorsMiddleware MUST be first +MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', # MUST BE FIRST for CORS to work + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'config.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + + +WSGI_APPLICATION = 'config.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'postgres_Intg_osdag', + 'USER': 'osdagdeveloper', + 'PASSWORD': 'password', + 'HOST': 'localhost', # This should be the name of the service + 'PORT': '5432', + } +} +# Password validation +# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +REST_FRAMEWORK = { + 'DEFAULT_PARSER_CLASSES': [ + 'rest_framework.parsers.JSONParser', + ], + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'apps.core.middleware.firebase_auth.FirebaseAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.AllowAny', + ), +} + + +# Internationalization +# https://docs.djangoproject.com/en/3.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.2/howto/static-files/ + +STATIC_URL = '/static/' +STATICFILES_DIRS = [ + BASE_DIR / "static" +] + +# OSI files storage +OSIFILES_URL = '/osifiles/' +OSIFILES_ROOT = BASE_DIR / 'osifiles' +# Default primary key field type +# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +MEDIA_ROOT = os.path.join(BASE_DIR, 'file_storage/') + +SECRET_ROOT = os.path.join(BASE_DIR , 'secret/') + diff --git a/backend/config/urls.py b/backend/config/urls.py new file mode 100644 index 000000000..00ad89021 --- /dev/null +++ b/backend/config/urls.py @@ -0,0 +1,16 @@ +# django imports +from django.contrib import admin +from django.urls import path +from django.urls import include +from django.conf import settings +from django.conf.urls.static import static + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include('apps.core.urls')), # Core app URLs (replaces osdag.urls) + path('api/modules/', include('apps.modules.urls')), # All module URLs aggregated here +] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + +if settings.DEBUG: + urlpatterns += static(settings.OSIFILES_URL, document_root=settings.OSIFILES_ROOT) + diff --git a/backend/config/utils.py b/backend/config/utils.py new file mode 100644 index 000000000..7bcdd9484 --- /dev/null +++ b/backend/config/utils.py @@ -0,0 +1,6 @@ +HOST = "smtp-mail.outlook.com" +PORT = 587 + +FROM_EMAIL = "osdagoncloud3@outlook.com" +PASSWORD = "osdag.developer" + diff --git a/backend/config/wsgi.py b/backend/config/wsgi.py new file mode 100644 index 000000000..3eb9bd3f4 --- /dev/null +++ b/backend/config/wsgi.py @@ -0,0 +1,17 @@ +""" +WSGI config for osdag_on_web project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + +application = get_wsgi_application() + diff --git a/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/3d.png b/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/3d.png new file mode 100644 index 000000000..a931b6087 Binary files /dev/null and b/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/3d.png differ diff --git a/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/front.png b/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/front.png new file mode 100644 index 000000000..154da4ec2 Binary files /dev/null and b/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/front.png differ diff --git a/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/side.png b/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/side.png new file mode 100644 index 000000000..aa96dfb25 Binary files /dev/null and b/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/side.png differ diff --git a/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/top.png b/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/top.png new file mode 100644 index 000000000..eafc819a8 Binary files /dev/null and b/backend/file_storage/design_report/JVelxm3R5am8JTLj/ResourceFiles/images/top.png differ diff --git a/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/3d.png b/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/3d.png new file mode 100644 index 000000000..a931b6087 Binary files /dev/null and b/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/3d.png differ diff --git a/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/front.png b/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/front.png new file mode 100644 index 000000000..154da4ec2 Binary files /dev/null and b/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/front.png differ diff --git a/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/side.png b/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/side.png new file mode 100644 index 000000000..aa96dfb25 Binary files /dev/null and b/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/side.png differ diff --git a/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/top.png b/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/top.png new file mode 100644 index 000000000..eafc819a8 Binary files /dev/null and b/backend/file_storage/design_report/jtd27dV4jBPBCir1/ResourceFiles/images/top.png differ diff --git a/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/3d.png b/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/3d.png new file mode 100644 index 000000000..1115b4a4d Binary files /dev/null and b/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/3d.png differ diff --git a/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/front.png b/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/front.png new file mode 100644 index 000000000..5adea7c3a Binary files /dev/null and b/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/front.png differ diff --git a/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/side.png b/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/side.png new file mode 100644 index 000000000..281f031c3 Binary files /dev/null and b/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/side.png differ diff --git a/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/top.png b/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/top.png new file mode 100644 index 000000000..b274507bd Binary files /dev/null and b/backend/file_storage/design_report/tWsjzuW9ywbDJL1S/top.png differ diff --git a/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/3d.png b/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/3d.png new file mode 100644 index 000000000..1115b4a4d Binary files /dev/null and b/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/3d.png differ diff --git a/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/front.png b/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/front.png new file mode 100644 index 000000000..5adea7c3a Binary files /dev/null and b/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/front.png differ diff --git a/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/side.png b/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/side.png new file mode 100644 index 000000000..281f031c3 Binary files /dev/null and b/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/side.png differ diff --git a/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/top.png b/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/top.png new file mode 100644 index 000000000..b274507bd Binary files /dev/null and b/backend/file_storage/design_report/vYTUTlrcu8Vex55F/ResourceFiles/images/top.png differ diff --git a/backend/manage.py b/backend/manage.py new file mode 100644 index 000000000..08c3a678c --- /dev/null +++ b/backend/manage.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() + diff --git a/bc_endplate_test.py b/bc_endplate_test.py deleted file mode 100644 index 28f843426..000000000 --- a/bc_endplate_test.py +++ /dev/null @@ -1,58 +0,0 @@ -import yaml -from design_type.connection.beam_column_end_plate import BeamColumnEndPlate -from utils.common.component import * -from utils.common.load import Load - - - -# Load osi file -# Get input objects -# ''' -input_file_path = 'bcinput1.osi' -with open(input_file_path, 'r') as input_file: - design_dictionary = yaml.load(input_file, Loader=yaml.FullLoader) - -# print(design_dictionary) - -bcinput = BeamColumnEndPlate() -bcinput.set_input_values(design_dictionary) - - -''' -# Hardcoded inputs -bcinput = BeamColumnEndPlate() - -bcinput.mainmodule = "Moment Connection" -bcinput.connectivity = 'Column flange-Beam web' -bcinput.material = Material(material_grade='E 250 (Fe 410 W)A') - -bcinput.supporting_section = Column(designation='HB 400', - material_grade='E 250 (Fe 410 W)A') - -bcinput.supported_section = Beam(designation='MB 350', - material_grade='E 250 (Fe 410 W)A') -bcinput.bolt = Bolt(grade=[4.6, 8.8], diameter=[12, 20], - bolt_type='Friction Grip Bolt', - bolt_hole_type='Standard', - edge_type='a - Sheared or hand flame cut', - mu_f=0.3, - corrosive_influences='No', - bolt_tensioning='Pretensioned') - -bcinput.load = Load(shear_force=40, - axial_force=10, moment=50) - -bcinput.plate = Plate(thickness=[12, 220], - material_grade='E 250 (Fe 410 W)A', - gap=10) -bcinput.plate.design_status_capacity = False -bcinput.weld = Weld(material_g_o=410, - fabrication='Shop Weld') - -''' -print("input values are set. Doing preliminary member checks") -bcinput.warn_text() -# bcinput.member_capacity() -print(bcinput.endplate_type) -bcinput.trial_design() - diff --git a/cad/BBCad/nutBoltPlacement_AF.py b/cad/BBCad/nutBoltPlacement_AF.py deleted file mode 100644 index 989958c15..000000000 --- a/cad/BBCad/nutBoltPlacement_AF.py +++ /dev/null @@ -1,167 +0,0 @@ -""" -created on 25-02-2018 -@author: Siddhesh Chavan - -modified: Darshan Vishwakarma (10-1-2020) - -AF abbreviation used here is for Above Flange for bolting. -BF abbreviation used here is for Below Flange for bolting. -W is for bolting over Web. - -""""" - -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt -import doctest - - -class NutBoltArray_AF(): - def __init__(self, outputobj, nut, bolt, numOfboltsF, nutSpaceF): - """ - :param alist: Input values, entered by user - :param beam_data: Beam dimensions - :param outputobj: Output dictionary - :param nut: Nut dimensions - :param bolt: Bolt dimensions - :param numOfboltsF: Number of bolts required for over plate above flange - :param nutSpaceF: Spacing between bolt head and nut - """ - self.boltOrigin_AF = None - self.pitch_new_AF = None - self.originAF = None - self.gaugeDirAF = None - self.pitchDirAF = None - self.boltDirAF = None - - - self.bolt = bolt - self.nut = nut - self.outputobj = outputobj - self.numOfboltsF = numOfboltsF - self.nutSpaceF = nutSpaceF - - self.initBoltPlaceParams_AF(outputobj) - self.bolts_AF = [] - self.nuts_AF = [] - self.initialiseNutBolts_AF() - self.positions_AF = [] - self.models_AF = [] - -################################################################# -# Nut_Bolt placement above flange(AF) of beam # -################################################################# - - def initialiseNutBolts_AF(self): - ''' - :return: This initializes required number of bolts and nuts for above flange. - ''' - b_AF = self.bolt - n_AF = self.nut - - for i in range(self.numOfboltsF): - bolt_length_required = float(n_AF.H + self.nutSpaceF)#todo: anjali - b_AF.H = bolt_length_required + (bolt_length_required - 5) % 5 - self.bolts_AF.append(Bolt(b_AF.R, b_AF.T, b_AF.H, b_AF.r)) - # print("bolt", b_AF.R, b_AF.T, b_AF.H, b_AF.r) - self.nuts_AF.append(Nut(n_AF.R, n_AF.T, n_AF.H, n_AF.r1)) - # print('Nut',(n_AF.R, n_AF.T, n_AF.H, n_AF.r1)) - - def initBoltPlaceParams_AF(self, outputobj): - ''' - :param outputobj: This is output dictionary for bolt placement parameters - :return: Edge, end, gauge and pitch distances for placement - ''' - - self.edge_AF = outputobj.flange_plate.edge_dist_provided #33 - self.end_AF = outputobj.flange_plate.end_dist_provided #33 - self.edge_gauge_AF = outputobj.flange_plate.edge_dist_provided #33 - self.pitch_AF = outputobj.flange_plate.pitch_provided #50 - self.midpitch_AF = outputobj.flange_plate.midpitch - self.gauge_AF = outputobj.flange_plate.midgauge - self.gauge = outputobj.flange_plate.gauge_provided - - - self.row_AF = outputobj.flange_plate.bolt_line #2 - self.col_AF = outputobj.flange_plate.bolts_one_line #2 - self.gap = outputobj.flange_plate.gap - - # print('iniBoltPlaceParams_AF', (self.edge_AF, self.end_AF, self.edge_gauge_AF, self.pitch_AF, self.gauge_AF, self.row_AF, self.col_AF, self.gap)) - - def calculatePositions_AF(self): - """ - :return: The positions/coordinates to place the bolts in the form of list, positions_AF = [list of bolting coordinates] - """ - self.positions_AF = [] - self.boltOrigin_AF = self.originAF + self.end_AF * self.pitchDirAF + ((self.plateAbvFlangeL - self.gauge_AF)/2 - ((self.col_AF/2-1)*self.gauge)) * self.gaugeDirAF - - for rw_AF in range(self.row_AF): - for cl_AF in range(self.col_AF): - pos_AF = self.boltOrigin_AF - if self.row_AF / 2 < rw_AF or self.row_AF / 2 == rw_AF: - self.pitch_new_AF = self.midpitch_AF - pos_AF = pos_AF + ((rw_AF - 1) * self.pitch_AF + self.pitch_new_AF) * self.pitchDirAF - if self.col_AF / 2 > cl_AF: - pos_AF = pos_AF + cl_AF * self.gauge * self.gaugeDirAF - else: - pos_AF = pos_AF + (cl_AF-1) * self.gauge * self.gaugeDirAF + 1 * self.gauge_AF * self.gaugeDirAF - self.positions_AF.append(pos_AF) - else: - pos_AF = pos_AF + rw_AF * self.pitch_AF * self.pitchDirAF - if self.col_AF / 2 > cl_AF : - pos_AF = pos_AF + cl_AF * self.gauge * self.gaugeDirAF - else: - pos_AF = pos_AF + (cl_AF-1) * self.gauge * self.gaugeDirAF + 1 * self.gauge_AF * self.gaugeDirAF - self.positions_AF.append(pos_AF) - - def placeAF(self, originAF, gaugeDirAF, pitchDirAF, boltDirAF, plateAbvFlangeL): - ''' - places the bolts and nuts based on the defined bolt arrangement - ''' - self.originAF = originAF - self.gaugeDirAF = gaugeDirAF - self.pitchDirAF = pitchDirAF - self.boltDirAF = boltDirAF - self.plateAbvFlangeL = plateAbvFlangeL - - self.calculatePositions_AF() - - for index, pos in enumerate(self.positions_AF): - self.bolts_AF[index].place(pos, gaugeDirAF, boltDirAF) - self.nuts_AF[index].place((pos + self.nutSpaceF * boltDirAF), gaugeDirAF, boltDirAF) - - def create_modelAF(self): - - for bolt in self.bolts_AF: - self.models_AF.append(bolt.create_model()) - - for nut in self.nuts_AF: - self.models_AF.append(nut.create_model()) - pass - - dbg = self.dbgSphere(self.originAF) - self.models_AF.append(dbg) - - def dbgSphere(self, pt): - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_modelsAF(self): - return self.models_AF - - # Below methods are for creating holes in flange and web - def get_bolt_listLA(self): - boltlist = [] - for bolt in self.bolts_AF: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originAF) - self.models_AF.append(dbg) - return boltlist - - def get_bolt_listRA(self): - boltlist = [] - for bolt in self.bolts_AF: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originAF) - self.models_AF.append(dbg) - return boltlist diff --git a/cad/BBCad/nutBoltPlacement_BF.py b/cad/BBCad/nutBoltPlacement_BF.py deleted file mode 100644 index 51cbbf6bb..000000000 --- a/cad/BBCad/nutBoltPlacement_BF.py +++ /dev/null @@ -1,159 +0,0 @@ -""" -created on 25-02-2018 -@author: Siddhesh Chavan - -modified: Darshan Vishwakarma (10-1-2020) - -AF abbreviation used here is for Above Flange for bolting. -BF abbreviation used here is for Below Flange for bolting. -W is for bolting over Web. -""""" - - -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt - - -class NutBoltArray_BF(): - def __init__(self, outputobj, nut, bolt, numOfboltsF, nutSpaceF): - """ - :param alist: Input values, entered by user - :param beam_data: Beam dimensions - :param outputobj: Output dictionary - :param nut: Nut dimensions - :param bolt: Bolt dimensions - :param numOfboltsF: Number of bolts required for over plate above flange - :param nutSpaceF: Spacing between bolt head and nut - """ - self.boltOrigin_BF = None - self.pitch_new_BF = None - self.originBF = None - self.gaugeDirBF = None - self.pitchDirBF = None - self.boltDirBF = None - - self.bolt = bolt - self.nut = nut - self.outputobj = outputobj - self.numOfboltsF = numOfboltsF - self.nutSpaceF = nutSpaceF - - self.initBoltPlaceParams_BF(outputobj) - self.bolts_BF = [] - self.nuts_BF = [] - self.initialiseNutBolts_BF() - self.positions_BF = [] - self.models_BF = [] - -################################################################# -# Nut_Bolt placement below flange(BF) of beam # -################################################################# - - def initialiseNutBolts_BF(self): - ''' - :return: This initializes required number of bolts and nuts for below flange. - ''' - b_BF = self.bolt - n_BF = self.nut - for j in range(self.numOfboltsF): - bolt_length_required = float(n_BF.H + self.nutSpaceF) - b_BF.H = bolt_length_required + 10 - self.bolts_BF.append(Bolt(b_BF.R, b_BF.T, b_BF.H, b_BF.r)) - self.nuts_BF.append(Nut(n_BF.R, n_BF.T, n_BF.H, n_BF.r1)) - - def initBoltPlaceParams_BF(self, outputobj): - ''' - :param outputobj: This is output dictionary for bolt placement parameters - :return: Edge, end, gauge and pitch distances for placement - ''' - self.edge_BF = outputobj.flange_plate.edge_dist_provided - self.end_BF = outputobj.flange_plate.end_dist_provided - self.edge_gauge_BF = outputobj.flange_plate.edge_dist_provided - self.pitch_BF = outputobj.flange_plate.pitch_provided - self.gauge_BF = outputobj.flange_plate.midgauge - self.gauge = outputobj.flange_plate.gauge_provided - self.row_BF = outputobj.flange_plate.bolt_line - self.col_BF = outputobj.flange_plate.bolts_one_line - self.gap = outputobj.flange_plate.gap - - - def calculatePositions_BF(self): - """ - :return: The positions/coordinates to place the bolts in the form of list, positions_BF = [list of bolting coordinates] - """ - self.positions_BF = [] - self.boltOrigin_BF = self.originBF + self.end_BF * self.pitchDirBF + ((self.plateBelwFlangeL - self.gauge_BF) / 2 -((self.col_BF/2-1)*self.gauge)) * self.gaugeDirBF - - for rw_BF in range(self.row_BF): - for cl_BF in range(self.col_BF): - pos_BF = self.boltOrigin_BF - if self.row_BF / 2 < rw_BF or self.row_BF / 2 == rw_BF: - self.pitch_new_BF = 2 * self.end_BF + self.gap - pos_BF = pos_BF + ((rw_BF - 1) * self.pitch_BF + self.pitch_new_BF) * self.pitchDirBF - if self.col_BF / 2 > cl_BF: - pos_BF = pos_BF + cl_BF * self.gauge * self.gaugeDirBF - else: - pos_BF = pos_BF + ( - cl_BF - 1) * self.gauge * self.gaugeDirBF + 1 * self.gauge_BF * self.gaugeDirBF - self.positions_BF.append(pos_BF) - else: - pos_BF = pos_BF + rw_BF * self.pitch_BF * self.pitchDirBF - if self.col_BF / 2 > cl_BF: - pos_BF = pos_BF + cl_BF * self.gauge * self.gaugeDirBF - else: - pos_BF = pos_BF + ( - cl_BF - 1) * self.gauge * self.gaugeDirBF + 1 * self.gauge_BF * self.gaugeDirBF - self.positions_BF.append(pos_BF) - - def placeBF(self, originBF, gaugeDirBF, pitchDirBF, boltDirBF, plateBelwFlangeL): - ''' - places the bolts and nuts based on the defined bolt arrangement - ''' - self.originBF = originBF - self.gaugeDirBF = gaugeDirBF - self.pitchDirBF = pitchDirBF - self.boltDirBF = boltDirBF - self.plateBelwFlangeL = plateBelwFlangeL - - self.calculatePositions_BF() - - for index_BF, pos_BF in enumerate(self.positions_BF): - self.bolts_BF[index_BF].place(pos_BF, gaugeDirBF, boltDirBF) - self.nuts_BF[index_BF].place((pos_BF + self.nutSpaceF * boltDirBF), gaugeDirBF, boltDirBF) - - def create_modelBF(self): - for bolt in self.bolts_BF: - self.models_BF.append(bolt.create_model()) - pass - for nut in self.nuts_BF: - self.models_BF.append(nut.create_model()) - pass - - dbg = self.dbgSphere(self.originBF) - self.models_BF.append(dbg) - - def dbgSphere(self, pt): - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_modelsBF(self): - return self.models_BF - - # Below methods are for creating holes in flange and web - def get_bolt_listLB(self): - boltlist = [] - for bolt in self.bolts_BF: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originBF) - self.models_BF.append(dbg) - return boltlist - - def get_bolt_listRB(self): - boltlist = [] - for bolt in self.bolts_BF: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originBF) - self.models_BF.append(dbg) - return boltlist - diff --git a/cad/BBCad/nutBoltPlacement_Web.py b/cad/BBCad/nutBoltPlacement_Web.py deleted file mode 100644 index 194ae4bb1..000000000 --- a/cad/BBCad/nutBoltPlacement_Web.py +++ /dev/null @@ -1,145 +0,0 @@ -""" -created on 25-02-2018 -@author: Siddhesh Chavan - -modified: Darshan Vishwakarma (10-1-2020) - -AF abbreviation used here is for Above Flange for bolting. -BF abbreviation used here is for Below Flange for bolting. -W is for bolting over Web. -""""" - - -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt - - -class NutBoltArray_Web(): - def __init__(self, outputobj, nut, bolt, numOfboltsW, nutSpaceW): - """ - :param alist: Input values, entered by user - :param beam_data: Beam dimensions - :param outputobj: Output dictionary - :param nut: Nut dimensions - :param bolt: Bolt dimensions - :param numOfboltsF: Number of bolts required for over plate above flange - :param nutSpaceF: Spacing between bolt head and nut - """ - self.boltOrigin_W = None - self.originW = None - self.gaugeDirW = None - self.pitchDirW = None - self.boltDirW = None - - self.bolt = bolt - self.nut = nut - self.outputobj = outputobj - self.numOfboltsW = numOfboltsW - self.nutSpaceW = nutSpaceW - - - self.initBoltPlaceParams_Web(outputobj) - self.bolts_W = [] - self.nuts_W = [] - self.initialiseNutBolts_Web() - self.positions_W = [] - self.models_W = [] - -################################################################# -# Nut_Bolt placement over Web of beam # -################################################################# - - def initialiseNutBolts_Web(self): - ''' - :return: This initializes required number of bolts and nuts for web bolting. - ''' - b_W = self.bolt - n_W = self.nut - for k in range(self.numOfboltsW): - bolt_length_required = float(n_W.H + self.nutSpaceW) - b_W.H = bolt_length_required +10 - self.bolts_W.append(Bolt(b_W.R, b_W.T, b_W.H, b_W.r)) - self.nuts_W.append(Nut(n_W.R, n_W.T, n_W.H, n_W.r1)) - - def initBoltPlaceParams_Web(self, outputobj): - ''' - :param outputobj: This is output dictionary for bolt placement parameters - :return: Edge, end, gauge and pitch distances for placement - ''' - self.edge_W = outputobj.web_plate.edge_dist_provided #33 - self.end_W = outputobj.web_plate.end_dist_provided #33 - self.pitch_W = outputobj.web_plate.pitch_provided - self.pitch_MW = outputobj.web_plate.midpitch # todo for gap - self.gauge_W = outputobj.web_plate.gauge_provided - - self.row_W = outputobj.web_plate.bolts_one_line - self.col_W = outputobj.web_plate.bolt_line - - def calculatePositions_Web(self): - """ - - :return: The positions/coordinates to place the bolts in the form of list, positions_W = [list of bolting coordinates] - """ - self.positions_W = [] - self.boltOrigin_W = self.originW + self.end_W * self.pitchDirW + self.edge_W * self.gaugeDirW - for rw_W in range(self.row_W): - for cl_W in range(self.col_W): - pos_W = self.boltOrigin_W - pos_W = pos_W + rw_W * self.gauge_W * self.pitchDirW - print (self.gauge_W) - if self.col_W / 2 > cl_W: - pos_W = pos_W + cl_W * self.pitch_W * self.gaugeDirW - else: - pos_W = pos_W + (cl_W - 1) * self.pitch_W * self.gaugeDirW + 1 * self.pitch_MW * self.gaugeDirW - self.positions_W.append(pos_W) - - - def placeW(self, originW, gaugeDirW, pitchDirW, boltDirW): - """ - :param originW: Origin for bolt placement - :param gaugeDirW: Gauge direction for gauge distance - :param pitchDirW: Pitch direction for pitch distance - :param boltDirW: Bolt screwing direction - :return: places the bolts and nuts based on the defined bolt arrangement - - """ - self.originW = originW - self.gaugeDirW = gaugeDirW - self.pitchDirW = pitchDirW - self.boltDirW = boltDirW - - self.calculatePositions_Web() - - for index_W, pos_W in enumerate(self.positions_W): - self.bolts_W[index_W].place(pos_W, gaugeDirW, boltDirW) - self.nuts_W[index_W].place((pos_W + self.nutSpaceW * boltDirW), gaugeDirW, boltDirW) - - def create_modelW(self): - for bolt in self.bolts_W: - self.models_W.append(bolt.create_model()) - pass - for nut in self.nuts_W: - self.models_W.append(nut.create_model()) - pass - - dbg = self.dbgSphere(self.originW) - self.models_W.append(dbg) - - def dbgSphere(self, pt): - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_modelsW(self): - return self.models_W - - def get_bolt_web_list(self): - boltlist = [] - for bolt in self.bolts_W: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originW) - self.models_W.append(dbg) - return boltlist - - - diff --git a/cad/BasePlateCad/nutBoltPlacement.py b/cad/BasePlateCad/nutBoltPlacement.py deleted file mode 100644 index 7e534fc11..000000000 --- a/cad/BasePlateCad/nutBoltPlacement.py +++ /dev/null @@ -1,560 +0,0 @@ -''' -Created on 19-March-2020 - -@author : Anand Swaroop -''' - -from cad.items.anchor_bolt import * -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt -import copy - - -class NutBoltArray(): - """ - add a diagram here - """ - - def __init__(self, BP, nut, nut_in, bolt, bolt_in, nutSpace, washer, washer_in): - - - self.BP = BP - self.nut = nut - self.nut_in = nut_in - self.bolt = bolt - self.bolt_in = bolt_in - self.gap = nutSpace - self.washer = washer - self.washer_in = washer_in - self.origin = None - self.gaugeDir = None - self.pitchDir = None - self.boltDir = None - - self.noOfBolts_outFlange = self.BP.anchors_outside_flange - self.noofBolts_inFlange = self.BP.anchors_inside_flange - - self.ab1 = copy.deepcopy(self.bolt) - self.ab2 = copy.deepcopy(self.bolt) - self.ab3 = copy.deepcopy(self.bolt) - self.ab4 = copy.deepcopy(self.bolt) - - self.ab5 = copy.deepcopy(self.bolt) - self.ab6 = copy.deepcopy(self.bolt) - self.ab7 = copy.deepcopy(self.bolt) - self.ab8 = copy.deepcopy(self.bolt) - - self.ab9 = copy.deepcopy(self.bolt) - self.ab10 = copy.deepcopy(self.bolt) - self.ab11 = copy.deepcopy(self.bolt) - self.ab12 = copy.deepcopy(self.bolt) - - - self.ab_inflg1 = copy.deepcopy(self.bolt_in) - self.ab_inflg2 = copy.deepcopy(self.bolt_in) - - self.ab_inflg3 = copy.deepcopy(self.bolt_in) - self.ab_inflg4 = copy.deepcopy(self.bolt_in) - - self.ab_inflg5 = copy.deepcopy(self.bolt_in) - self.ab_inflg6 = copy.deepcopy(self.bolt_in) - self.ab_inflg7 = copy.deepcopy(self.bolt_in) - self.ab_inflg8 = copy.deepcopy(self.bolt_in) - - self.w1 = copy.deepcopy(self.washer) - self.w2 = copy.deepcopy(self.washer) - self.w3 = copy.deepcopy(self.washer) - self.w4 = copy.deepcopy(self.washer) - self.w5 = copy.deepcopy(self.washer) - self.w6 = copy.deepcopy(self.washer) - self.w7 = copy.deepcopy(self.washer) - self.w8 = copy.deepcopy(self.washer) - self.w9 = copy.deepcopy(self.washer) - self.w10 = copy.deepcopy(self.washer) - self.w11 = copy.deepcopy(self.washer) - self.w12 = copy.deepcopy(self.washer) - - self.w_in1 = copy.deepcopy(self.washer_in) - self.w_in2 = copy.deepcopy(self.washer_in) - self.w_in3 = copy.deepcopy(self.washer_in) - self.w_in4 = copy.deepcopy(self.washer_in) - self.w_in5 = copy.deepcopy(self.washer_in) - self.w_in6 = copy.deepcopy(self.washer_in) - self.w_in7 = copy.deepcopy(self.washer_in) - self.w_in8 = copy.deepcopy(self.washer_in) - - self.nt1 = copy.deepcopy(self.nut) - self.nt2 = copy.deepcopy(self.nut) - self.nt3 = copy.deepcopy(self.nut) - self.nt4 = copy.deepcopy(self.nut) - - self.nt5 = copy.deepcopy(self.nut) - self.nt6 = copy.deepcopy(self.nut) - self.nt7 = copy.deepcopy(self.nut) - self.nt8 = copy.deepcopy(self.nut) - - self.nt9 = copy.deepcopy(self.nut) - self.nt10 = copy.deepcopy(self.nut) - self.nt11 = copy.deepcopy(self.nut) - self.nt12 = copy.deepcopy(self.nut) - - self.nt_inflg1 = copy.deepcopy(self.nut_in) - self.nt_inflg2 = copy.deepcopy(self.nut_in) - - self.nt_inflg3 = copy.deepcopy(self.nut_in) - self.nt_inflg4 = copy.deepcopy(self.nut_in) - - self.nt_inflg5 = copy.deepcopy(self.nut_in) - self.nt_inflg6 = copy.deepcopy(self.nut_in) - self.nt_inflg7 = copy.deepcopy(self.nut_in) - self.nt_inflg8 = copy.deepcopy(self.nut_in) - # self.initBoltPlaceParam(plateObj) - self.initBoltPlaceParam() - - self.bolts = [] - self.nuts = [] - - self.positions = [] - - self.models = [] - - self.initialiseNutBolts() - - def initialiseNutBolts(self): - """ - Initializing the Nut and Bolt - :return: - """ - pass - - - def initBoltPlaceParam(self): - self.enddist = self.BP.end_distance_out - self.edgedist = self.BP.edge_distance_out - # self.clearence = 50 - - self.pitch = self.BP.bp_length_provided - 2 * self.enddist - self.gauge = self.BP.bp_width_provided - 2 * self.edgedist - - self.pitch1 = self.BP.pitch_distance_out - self.gauge1 = self.BP.gauge_distance_out - - if self.BP.load_axial_tension > 0 or self.BP.load_moment_minor > 0: - self.enddist_in = self.BP.end_distance_in - self.edgedist_in = self.BP.edge_distance_in - if self.BP.anchors_inside_flange == 8: - self.pitch_in = self.BP.pitch_distance_in - self.gauge_in = self.BP.gauge_distance_in - - if self.BP.connectivity != 'Hollow/Tubular Column Base': - if self.BP.stiffener_across_web != 'Yes': - self.BP.stiffener_plt_thick_across_web = 0 - self.stiffener_inflg_thickness = self.BP.stiffener_plt_thick_across_web - self.pitch_inflg = (self.BP.column_D - (2*self.BP.column_tf + 2*self.BP.column_r1 + self.stiffener_inflg_thickness))/4 - self.web_thick = self.BP.column_tw/2 - - - def calculatePositions(self): - pass - - - def place(self, origin, gaugeDir, pitchDir, boltDir): - self.origin = origin - self.gaugeDir = gaugeDir - self.pitchDir = pitchDir - self.boltDir = boltDir - - # self.calculatePositions() - pos = self.origin - pos1 = pos + self.edgedist * self.gaugeDir + self.enddist * self.pitchDir #bottom left - pos2 = pos1 + self.gauge * self.gaugeDir #bottom right - pos3 = pos2 + self.pitch * self.pitchDir #top left - pos4 = pos3 - self.gauge * self.gaugeDir #top right - - - - self.ab1.place(pos1 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab2.place(pos2 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab3.place(pos3 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab4.place(pos4 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.nt1.place(pos1 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt2.place(pos2 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt3.place(pos3 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt4.place(pos4 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.w1.place(pos1 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w2.place(pos2 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w3.place(pos3 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w4.place(pos4 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - if 2*self.BP.anchors_outside_flange == 6 : - pos5 = pos2 - self.gauge/2 * self.gaugeDir - pos6 = pos4 + self.gauge/2 * self.gaugeDir - - self.ab5.place(pos5 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab6.place(pos6 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.nt5.place(pos5 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt6.place(pos6 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.w5.place(pos5 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w6.place(pos6 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - if 2*self.BP.anchors_outside_flange == 8: - pos5 = pos1 + self.pitch1 * self.pitchDir - pos6 = pos2 + self.pitch1 * self.pitchDir - pos7 = pos3 - self.pitch1 * self.pitchDir - pos8 = pos4 - self.pitch1 * self.pitchDir - - self.ab5.place(pos5 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab6.place(pos6 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab7.place(pos7 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab8.place(pos8 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.nt5.place(pos5 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt6.place(pos6 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt7.place(pos7 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt8.place(pos8 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.w5.place(pos5 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w6.place(pos6 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w7.place(pos7 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w8.place(pos8 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - if 2*self.BP.anchors_outside_flange == 12: - pos5 = pos1 + self.pitch1 * self.pitchDir - pos6 = pos2 + self.pitch1 * self.pitchDir - pos7 = pos3 - self.pitch1 * self.pitchDir - pos8 = pos4 - self.pitch1 * self.pitchDir - - pos9 = pos2 - self.gauge / 2 * self.gaugeDir - pos10 = pos4 + self.gauge / 2 * self.gaugeDir - pos11 = pos9 + self.pitch1 * self.pitchDir - pos12 = pos10 - self.pitch1 * self.pitchDir - - self.ab5.place(pos5 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab6.place(pos6 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab7.place(pos7 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab8.place(pos8 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.ab9.place(pos9 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab10.place(pos10 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab11.place(pos11 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab12.place(pos12 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.nt5.place(pos5 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt6.place(pos6 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt7.place(pos7 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt8.place(pos8 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.nt9.place(pos9 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt10.place(pos10 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt11.place(pos11 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt12.place(pos12 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.w5.place(pos5 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w6.place(pos6 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w7.place(pos7 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w8.place(pos8 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.w9.place(pos9 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w10.place(pos10 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w11.place(pos11 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w12.place(pos12 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - if self.BP.anchors_inside_flange == 2: - - pos_inflg_1 = pos2 + self.pitch/2 * self.pitchDir + (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - pos_inflg_2 = pos4 - self.pitch/2 * self.pitchDir - (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - - self.ab_inflg1.place(pos_inflg_1 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg2.place(pos_inflg_2 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.nt_inflg1.place(pos_inflg_1 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg2.place(pos_inflg_2 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.w_in1.place(pos_inflg_1 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in2.place(pos_inflg_2 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - if self.BP.anchors_inside_flange == 4: - - pos_inflg_1 = pos2 + self.pitch/2 * self.pitchDir - self.pitch_inflg * self.pitchDir + (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - pos_inflg_2 = pos4 - self.pitch/2 * self.pitchDir - self.pitch_inflg * self.pitchDir - (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - pos_inflg_3 = pos2 + self.pitch/2 * self.pitchDir + self.pitch_inflg * self.pitchDir + (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - pos_inflg_4 = pos4 - self.pitch/2 * self.pitchDir + self.pitch_inflg * self.pitchDir - (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - - self.ab_inflg1.place(pos_inflg_1 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg2.place(pos_inflg_2 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg3.place(pos_inflg_3 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg4.place(pos_inflg_4 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.nt_inflg1.place(pos_inflg_1 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg2.place(pos_inflg_2 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg3.place(pos_inflg_3 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg4.place(pos_inflg_4 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.w_in1.place(pos_inflg_1 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in2.place(pos_inflg_2 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in3 .place(pos_inflg_3 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in4.place(pos_inflg_4 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - if self.BP.anchors_inside_flange == 8: - - pos_inflg_1 = pos2 + self.pitch/2 * self.pitchDir - self.pitch_inflg * self.pitchDir + (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - pos_inflg_2 = pos4 - self.pitch/2 * self.pitchDir - self.pitch_inflg * self.pitchDir - (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - pos_inflg_3 = pos2 + self.pitch/2 * self.pitchDir + self.pitch_inflg * self.pitchDir + (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - pos_inflg_4 = pos4 - self.pitch/2 * self.pitchDir + self.pitch_inflg * self.pitchDir - (self.edgedist_in-self.gauge / 2 + self.web_thick) * self.gaugeDir - - pos_inflg_5 = pos2 + self.pitch / 2 * self.pitchDir - self.pitch_inflg * self.pitchDir + (self.edgedist_in - self.gauge / 2 + self.web_thick + self.gauge_in) * self.gaugeDir - pos_inflg_6 = pos4 - self.pitch / 2 * self.pitchDir - self.pitch_inflg * self.pitchDir - (self.edgedist_in - self.gauge / 2 + self.web_thick + self.gauge_in) * self.gaugeDir - pos_inflg_7 = pos2 + self.pitch / 2 * self.pitchDir + self.pitch_inflg * self.pitchDir + (self.edgedist_in - self.gauge / 2 + self.web_thick + self.gauge_in) * self.gaugeDir - pos_inflg_8 = pos4 - self.pitch / 2 * self.pitchDir + self.pitch_inflg * self.pitchDir - (self.edgedist_in - self.gauge / 2 + self.web_thick + self.gauge_in) * self.gaugeDir - - self.ab_inflg1.place(pos_inflg_1 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg2.place(pos_inflg_2 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg3.place(pos_inflg_3 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg4.place(pos_inflg_4 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.ab_inflg5.place(pos_inflg_5 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg6.place(pos_inflg_6 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg7.place(pos_inflg_7 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.ab_inflg8.place(pos_inflg_8 - (self.bolt.ex) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.nt_inflg1.place(pos_inflg_1 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg2.place(pos_inflg_2 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg3.place(pos_inflg_3 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg4.place(pos_inflg_4 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.nt_inflg5.place(pos_inflg_5 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg6.place(pos_inflg_6 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg7.place(pos_inflg_7 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.nt_inflg8.place(pos_inflg_8 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.w_in1.place(pos_inflg_1 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in2.place(pos_inflg_2 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in3 .place(pos_inflg_3 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in4.place(pos_inflg_4 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - self.w_in5.place(pos_inflg_5 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in6.place(pos_inflg_6 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in7.place(pos_inflg_7 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - self.w_in8.place(pos_inflg_8 - (self.nt1.T + 50) * numpy.array([0, 0, 1.0]), gaugeDir, boltDir) - - - - def create_model(self): - # for bolt in self.bolts: - # self.models.append(bolt.create_model()) - - self.ab1Model = self.ab1.create_model() - self.ab2Model = self.ab2.create_model() - self.ab3Model = self.ab3.create_model() - self.ab4Model = self.ab4.create_model() - - self.nt1Model = self.nt1.create_model() - self.nt2Model = self.nt2.create_model() - self.nt3Model = self.nt3.create_model() - self.nt4Model = self.nt4.create_model() - - self.w1Model = self.w1.create_model() - self.w2Model = self.w2.create_model() - self.w3Model = self.w3.create_model() - self.w4Model = self.w4.create_model() - - self.models = [self.ab1Model, self.ab2Model, self.ab3Model, self.ab4Model, self.nt1Model, self.nt2Model, self.nt3Model, self.nt4Model, self.w1Model, self.w2Model, self.w3Model, self.w4Model] - - if 2* self.BP.anchors_outside_flange == 6: - self.ab5Model = self.ab5.create_model() - self.ab6Model = self.ab6.create_model() - - self.nt5Model = self.nt5.create_model() - self.nt6Model = self.nt6.create_model() - - self.w5Model = self.w5.create_model() - self.w6Model = self.w6.create_model() - - models = [self.ab5Model, self.ab6Model,self.w5Model, self.w6Model, self.nt5Model, self.nt6Model] - self.models.extend(models) - - if 2* self.BP.anchors_outside_flange == 8: - self.ab5Model = self.ab5.create_model() - self.ab6Model = self.ab6.create_model() - self.ab7Model = self.ab7.create_model() - self.ab8Model = self.ab8.create_model() - - self.nt5Model = self.nt5.create_model() - self.nt6Model = self.nt6.create_model() - self.nt7Model = self.nt7.create_model() - self.nt8Model = self.nt8.create_model() - - self.w5Model = self.w5.create_model() - self.w6Model = self.w6.create_model() - self.w7Model = self.w7.create_model() - self.w8Model = self.w8.create_model() - - models = [self.ab5Model, self.ab6Model, self.ab7Model, self.ab8Model, self.w5Model, self.w6Model, self.w7Model, self.w8Model, self.nt5Model, self.nt6Model, self.nt7Model, self.nt8Model] - self.models.extend(models) - - if 2* self.BP.anchors_outside_flange == 12: - self.ab5Model = self.ab5.create_model() - self.ab6Model = self.ab6.create_model() - self.ab7Model = self.ab7.create_model() - self.ab8Model = self.ab8.create_model() - - self.ab9Model = self.ab9.create_model() - self.ab10Model = self.ab10.create_model() - self.ab11Model = self.ab11.create_model() - self.ab12Model = self.ab12.create_model() - - self.nt5Model = self.nt5.create_model() - self.nt6Model = self.nt6.create_model() - self.nt7Model = self.nt7.create_model() - self.nt8Model = self.nt8.create_model() - - self.nt9Model = self.nt9.create_model() - self.nt10Model = self.nt10.create_model() - self.nt11Model = self.nt11.create_model() - self.nt12Model = self.nt12.create_model() - - self.w5Model = self.w5.create_model() - self.w6Model = self.w6.create_model() - self.w7Model = self.w7.create_model() - self.w8Model = self.w8.create_model() - - self.w9Model = self.w9.create_model() - self.w10Model = self.w10.create_model() - self.w11Model = self.w11.create_model() - self.w12Model = self.w12.create_model() - - models = [self.ab5Model, self.ab6Model, self.ab7Model, self.ab8Model, self.ab9Model, self.ab10Model, self.ab11Model, self.ab12Model, - self.nt5Model, self.nt6Model, self.nt7Model, self.nt8Model, self.nt9Model, self.nt10Model, self.nt11Model, self.nt12Model, - self.w5Model, self.w6Model, self.w7Model, self.w8Model, self.w9Model, self.w10Model, self.w11Model, self.w12Model] - self.models.extend(models) - - if self.BP.anchors_inside_flange == 2: - self.ab_inflg1Model = self.ab_inflg1.create_model() - self.ab_inflg2Model = self.ab_inflg2.create_model() - self.nt_inflg1Model = self.nt_inflg1.create_model() - self.nt_inflg2Model = self.nt_inflg2.create_model() - - self.w_in1Model = self.w_in1.create_model() - self.w_in2Model = self.w_in2.create_model() - - models = [ self.ab_inflg1Model, self.ab_inflg2Model, self.w_in1Model, self.w_in2Model, self.nt_inflg1Model, self.nt_inflg2Model] - self.models.extend(models) - - if self.BP.anchors_inside_flange == 4: - self.ab_inflg1Model = self.ab_inflg1.create_model() - self.ab_inflg2Model = self.ab_inflg2.create_model() - self.nt_inflg1Model = self.nt_inflg1.create_model() - self.nt_inflg2Model = self.nt_inflg2.create_model() - self.w_in1Model = self.w_in1.create_model() - self.w_in2Model = self.w_in2.create_model() - - self.ab_inflg3Model = self.ab_inflg3.create_model() - self.ab_inflg4Model = self.ab_inflg4.create_model() - self.nt_inflg3Model = self.nt_inflg3.create_model() - self.nt_inflg4Model = self.nt_inflg4.create_model() - self.w_in3Model = self.w_in3.create_model() - self.w_in4Model = self.w_in4.create_model() - - models = [ self.ab_inflg1Model, self.ab_inflg2Model, self.ab_inflg3Model, self.ab_inflg4Model, - self.nt_inflg1Model, self.nt_inflg2Model, self.nt_inflg3Model, self.nt_inflg4Model, - self.w_in1Model, self.w_in2Model, self.w_in3Model, self.w_in4Model] - self.models.extend(models) - - if self.BP.anchors_inside_flange == 8: - self.ab_inflg1Model = self.ab_inflg1.create_model() - self.ab_inflg2Model = self.ab_inflg2.create_model() - self.nt_inflg1Model = self.nt_inflg1.create_model() - self.nt_inflg2Model = self.nt_inflg2.create_model() - self.w_in1Model = self.w_in1.create_model() - self.w_in2Model = self.w_in2.create_model() - - self.ab_inflg5Model = self.ab_inflg5.create_model() - self.ab_inflg6Model = self.ab_inflg6.create_model() - self.nt_inflg5Model = self.nt_inflg5.create_model() - self.nt_inflg6Model = self.nt_inflg6.create_model() - self.w_in5Model = self.w_in5.create_model() - self.w_in6Model = self.w_in6.create_model() - - self.ab_inflg3Model = self.ab_inflg3.create_model() - self.ab_inflg4Model = self.ab_inflg4.create_model() - self.nt_inflg3Model = self.nt_inflg3.create_model() - self.nt_inflg4Model = self.nt_inflg4.create_model() - self.w_in3Model = self.w_in3.create_model() - self.w_in4Model = self.w_in4.create_model() - - self.ab_inflg7Model = self.ab_inflg7.create_model() - self.ab_inflg8Model = self.ab_inflg8.create_model() - self.nt_inflg7Model = self.nt_inflg7.create_model() - self.nt_inflg8Model = self.nt_inflg8.create_model() - self.w_in7Model = self.w_in7.create_model() - self.w_in8Model = self.w_in8.create_model() - - models = [ self.ab_inflg1Model, self.ab_inflg2Model, self.ab_inflg3Model, self.ab_inflg4Model, - self.ab_inflg5Model, self.ab_inflg6Model, self.ab_inflg7Model, self.ab_inflg8Model, - self.nt_inflg1Model, self.nt_inflg2Model, self.nt_inflg3Model, self.nt_inflg4Model, - self.nt_inflg5Model, self.nt_inflg6Model, self.nt_inflg7Model, self.nt_inflg8Model, - self.w_in1Model, self.w_in2Model, self.w_in3Model, self.w_in4Model, - self.w_in5Model, self.w_in6Model, self.w_in7Model, self.w_in8Model - ] - self.models.extend(models) - - - def get_models(self): - return self.models - - -if __name__ == '__main__': - - from cad.items.anchor_bolt import * - from cad.items.nut import Nut - from cad.items.ISection import ISection - from cad.items.plate import Plate - - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - nutboltArrayOrigin = numpy.array([0., 0., 0.]) - gaugeDir = numpy.array([1.0, 0, 0]) - pitchDir = numpy.array([0, 1.0, 0]) - boltDir = numpy.array([0, 0, 1.0]) - - numberOfBolts = 6 - column = ISection(B=250, T=13.7, D=450, t=9.8, R1=14.0, R2=7.0, alpha=94, length=1500, notchObj=None) - baseplate = Plate(L=700, W=500, T=30) - - l = 550 - c = 225 - a = 175 - r = 24 - ex_length = (50 + 24 + baseplate.T) # nut.T = 24 - bolt = AnchorBolt_A(l=250, c=125, a=75, r=12, ex=ex_length) - # bolt = AnchorBolt_B(l= 250, c= 125, a= 75, r= 12) - # bolt = AnchorBolt_Endplate(l= 250, c= 125, a= 75, r= 12) - - nut = Nut(R=bolt.r * 3, T=24, H=30, innerR1=bolt.r) - - nutSpace = bolt.c + baseplate.T - - nut_bolt_array = NutBoltArray(column, baseplate, nut, bolt, numberOfBolts, nutSpace) - - place = nut_bolt_array.place(nutboltArrayOrigin, gaugeDir, pitchDir, boltDir) - - nut_bolt_array_Model = nut_bolt_array.create_model() - - nut_bolts = nut_bolt_array.get_models() - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - display.DisplayShape(array, update=True) - display.DisableAntiAliasing() - start_display() - diff --git a/cad/MomentConnections/BBSpliceCoverlateCAD/WeldedCAD.py b/cad/MomentConnections/BBSpliceCoverlateCAD/WeldedCAD.py deleted file mode 100644 index e9c9b6a7f..000000000 --- a/cad/MomentConnections/BBSpliceCoverlateCAD/WeldedCAD.py +++ /dev/null @@ -1,596 +0,0 @@ -""" -created on 17-04-2020 - -""" - -import numpy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse, BRepAlgoAPI_Cut -from cad.items.plate import Plate -import copy - - -class BBSpliceCoverPlateWeldedCAD(object): - def __init__(self, B, beam, flangePlate, innerFlangePlate, webPlate, flangePlateWeldL, flangePlateWeldW, - innerflangePlateWeldL, innerflangePlateWeldW, webPlateWeldL, webPlateWeldW): - - self.B = B - self.beam = beam - self.flangePlate = flangePlate - self.innerFlangePlate = innerFlangePlate - self.webPlate = webPlate - self.flangePlateWeldL = flangePlateWeldL - self.flangePlateWeldW = flangePlateWeldW - self.webPlateWeldL = webPlateWeldL - self.webPlateWeldW = webPlateWeldW - self.innerflangePlateWeldL = innerflangePlateWeldL - self.innerflangePlateWeldW = innerflangePlateWeldW - - self.gap = float(self.B.flange_plate.gap) - self.flangespace = float(self.B.flangespace) - self.webspace = float(self.B.webspace) - - self.beam1 = copy.deepcopy(self.beam) - self.beam2 = copy.deepcopy(self.beam) - - self.flangePlate1 = copy.deepcopy(self.flangePlate) - self.flangePlate2 = copy.deepcopy(self.flangePlate) - - self.innerFlangePlate1 = copy.deepcopy(self.innerFlangePlate) - self.innerFlangePlate2 = copy.deepcopy(self.innerFlangePlate) - self.innerFlangePlate3 = copy.deepcopy(self.innerFlangePlate) - self.innerFlangePlate4 = copy.deepcopy(self.innerFlangePlate) - - self.webPlate1 = copy.deepcopy(self.webPlate) - self.webPlate2 = copy.deepcopy(self.webPlate) - - # nuber top to bottom - self.flangePlateWeldL11 = copy.deepcopy(self.flangePlateWeldL) - self.flangePlateWeldL12 = copy.deepcopy(self.flangePlateWeldL) - self.flangePlateWeldL21 = copy.deepcopy(self.flangePlateWeldL) - self.flangePlateWeldL22 = copy.deepcopy(self.flangePlateWeldL) - - self.flangePlateWeldW11 = copy.deepcopy(self.flangePlateWeldW) - self.flangePlateWeldW12 = copy.deepcopy(self.flangePlateWeldW) - self.flangePlateWeldW21 = copy.deepcopy(self.flangePlateWeldW) - self.flangePlateWeldW22 = copy.deepcopy(self.flangePlateWeldW) - - # Todo: update numbering - self.webPlateWeldL11 = copy.deepcopy(self.webPlateWeldL) - self.webPlateWeldL12 = copy.deepcopy(self.webPlateWeldL) - self.webPlateWeldL21 = copy.deepcopy(self.webPlateWeldL) - self.webPlateWeldL22 = copy.deepcopy(self.webPlateWeldL) - - self.webPlateWeldW11 = copy.deepcopy(self.webPlateWeldW) - self.webPlateWeldW12 = copy.deepcopy(self.webPlateWeldW) - self.webPlateWeldW21 = copy.deepcopy(self.webPlateWeldW) - self.webPlateWeldW22 = copy.deepcopy(self.webPlateWeldW) - - # numbering is clock wise starting from right side top plate - self.innerflangePlateWeldL11 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL12 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL21 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL22 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL31 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL32 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL41 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL42 = copy.deepcopy(self.innerflangePlateWeldL) - - self.innerflangePlateWeldW11 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW12 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW21 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW22 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW31 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW32 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW41 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW42 = copy.deepcopy(self.innerflangePlateWeldW) - - self.weldCutPlate = Plate(L=self.beam.D + 4 * self.flangePlate.T, W=self.beam.B + 2 * self.flangePlate.T, - T=self.gap) - - def create_3DModel(self): - ''' - :return: CAD model of each of the followings. Debugging each command below would give give clear picture - ''' - - self.createbeamGeometry() - self.createPlateGeometry() - self.createWeldedGeometry() - - def createbeamGeometry(self): - """ - - :return: Geometric Orientation of this component - """ - beam1Origin = numpy.array([self.gap / 2, 0.0, 0.0]) - beam1_uDir = numpy.array([0.0, 1.0, 0.0]) - beam1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.beam1.place(beam1Origin, beam1_uDir, beam1_wDir) - - self.beam1Model = self.beam1.create_model() - - beam2Origin = numpy.array([-self.gap / 2, 0.0, 0.0]) - beam2_uDir = numpy.array([0.0, 1.0, 0.0]) - beam2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.beam2.place(beam2Origin, beam2_uDir, beam2_wDir) - - self.beam2Model = self.beam2.create_model() - - def createPlateGeometry(self): - - # Flange Plates - flangePlate1Origin = numpy.array([0.0, self.flangePlate.W / 2, (self.beam.D + self.flangePlate.T) / 2]) - flangePlate1_uDir = numpy.array([0.0, 0.0, 1.0]) - flangePlate1_wDir = numpy.array([0.0, -1.0, 0.0]) - self.flangePlate1.place(flangePlate1Origin, flangePlate1_uDir, flangePlate1_wDir) - - self.flangePlate1Model = self.flangePlate1.create_model() - - flangePlate2Origin = numpy.array([0.0, -self.flangePlate.W / 2, -(self.beam.D + self.flangePlate.T) / 2]) - flangePlate2_uDir = numpy.array([0.0, 0.0, 1.0]) - flangePlate2_wDir = numpy.array([0.0, 1.0, 0.0]) - self.flangePlate2.place(flangePlate2Origin, flangePlate2_uDir, flangePlate2_wDir) - - self.flangePlate2Model = self.flangePlate2.create_model() - - # Web Plates - webPlate1Origin = numpy.array([0.0, (self.beam.t + self.webPlate.T) / 2, -self.webPlate.W / 2]) - webPlate1_uDir = numpy.array([0.0, 1.0, 0.0]) - webPlate1_wDir = numpy.array([0.0, 0.0, 1.0]) - self.webPlate1.place(webPlate1Origin, webPlate1_uDir, webPlate1_wDir) - - self.webPlate1Model = self.webPlate1.create_model() - - webPlate2Origin = numpy.array([0.0, -(self.beam.t + self.webPlate.T) / 2, -self.webPlate.W / 2]) - webPlate2_uDir = numpy.array([0.0, 1.0, 0.0]) - webPlate2_wDir = numpy.array([0.0, 0.0, 1.0]) - self.webPlate2.place(webPlate2Origin, webPlate2_uDir, webPlate2_wDir) - - self.webPlate2Model = self.webPlate2.create_model() - - # Todo: Add an if statement for inner plates - # Inner Plates - if self.B.preference != 'Outside': - innerFlangePlatespacing = self.flangespace + self.beam.t / 2 + self.beam.R1 - innerFlangePlate1Origin = numpy.array( - [0.0, innerFlangePlatespacing, (self.beam.D - self.innerFlangePlate.T) / 2 - self.beam.T]) - innerFlangePlate1_uDir = numpy.array([0.0, 0.0, 1.0]) - innerFlangePlate1_wDir = numpy.array([0.0, 1.0, 0.0]) - self.innerFlangePlate1.place(innerFlangePlate1Origin, innerFlangePlate1_uDir, innerFlangePlate1_wDir) - - self.innerFlangePlate1Model = self.innerFlangePlate1.create_model() - - innerFlangePlate2Origin = numpy.array( - [0.0, innerFlangePlatespacing, -(self.beam.D - self.innerFlangePlate.T) / 2 + self.beam.T]) - innerFlangePlate2_uDir = numpy.array([0.0, 0.0, 1.0]) - innerFlangePlate2_wDir = numpy.array([0.0, 1.0, 0.0]) - self.innerFlangePlate2.place(innerFlangePlate2Origin, innerFlangePlate2_uDir, innerFlangePlate2_wDir) - - self.innerFlangePlate2Model = self.innerFlangePlate2.create_model() - - innerFlangePlate3Origin = numpy.array( - [0.0, -innerFlangePlatespacing, -(self.beam.D - self.innerFlangePlate.T) / 2 + self.beam.T]) - innerFlangePlate3_uDir = numpy.array([0.0, 0.0, 1.0]) - innerFlangePlate3_wDir = numpy.array([0.0, -1.0, 0.0]) - self.innerFlangePlate3.place(innerFlangePlate3Origin, innerFlangePlate3_uDir, innerFlangePlate3_wDir) - - self.innerFlangePlate3Model = self.innerFlangePlate3.create_model() - - innerFlangePlate4Origin = numpy.array( - [0.0, -innerFlangePlatespacing, (self.beam.D - self.innerFlangePlate.T) / 2 - self.beam.T]) - innerFlangePlate4_uDir = numpy.array([0.0, 0.0, 1.0]) - innerFlangePlate4_wDir = numpy.array([0.0, -1.0, 0.0]) - self.innerFlangePlate4.place(innerFlangePlate4Origin, innerFlangePlate4_uDir, innerFlangePlate4_wDir) - - self.innerFlangePlate4Model = self.innerFlangePlate4.create_model() - - def createWeldedGeometry(self): - - # Flangeplate1 - flangePlateWeldL11Origin = numpy.array( - [-self.flangePlateWeldL.L / 2, self.flangePlate.W / 2, (self.beam.D) / 2]) - flangePlateWeldL11_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlateWeldL11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.flangePlateWeldL11.place(flangePlateWeldL11Origin, flangePlateWeldL11_uDir, flangePlateWeldL11_wDir) - - self.flangePlateWeldL11Model = self.flangePlateWeldL11.create_model() - - flangePlateWeldL12Origin = numpy.array( - [self.flangePlateWeldL.L / 2, -self.flangePlate.W / 2, (self.beam.D) / 2]) - flangePlateWeldL12_uDir = numpy.array([0.0, -1.0, 0.0]) - flangePlateWeldL12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.flangePlateWeldL12.place(flangePlateWeldL12Origin, flangePlateWeldL12_uDir, flangePlateWeldL12_wDir) - - self.flangePlateWeldL12Model = self.flangePlateWeldL12.create_model() - - flangePlateWeldW11Origin = numpy.array([self.flangePlate.L / 2, -self.flangePlate.W / 2, (self.beam.D) / 2]) - flangePlateWeldW11_uDir = numpy.array([0.0, 0.0, 1.0]) - flangePlateWeldW11_wDir = numpy.array([0.0, 1.0, 0.0]) - self.flangePlateWeldW11.place(flangePlateWeldW11Origin, flangePlateWeldW11_uDir, flangePlateWeldW11_wDir) - - self.flangePlateWeldW11Model = self.flangePlateWeldW11.create_model() - - flangePlateWeldW12Origin = numpy.array([-self.flangePlate.L / 2, self.flangePlate.W / 2, (self.beam.D) / 2]) - flangePlateWeldW12_uDir = numpy.array([0.0, 0.0, 1.0]) - flangePlateWeldW12_wDir = numpy.array([0.0, -1.0, 0.0]) - self.flangePlateWeldW12.place(flangePlateWeldW12Origin, flangePlateWeldW12_uDir, flangePlateWeldW12_wDir) - - self.flangePlateWeldW12Model = self.flangePlateWeldW12.create_model() - - # FlangePlate2 - flangePlateWeldL21Origin = numpy.array( - [self.flangePlateWeldL.L / 2, self.flangePlate.W / 2, -(self.beam.D) / 2]) - flangePlateWeldL21_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlateWeldL21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.flangePlateWeldL21.place(flangePlateWeldL21Origin, flangePlateWeldL21_uDir, flangePlateWeldL21_wDir) - - self.flangePlateWeldL21Model = self.flangePlateWeldL21.create_model() - - flangePlateWeldL22Origin = numpy.array( - [-self.flangePlateWeldL.L / 2, -self.flangePlate.W / 2, -(self.beam.D) / 2]) - flangePlateWeldL22_uDir = numpy.array([0.0, -1.0, 0.0]) - flangePlateWeldL22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.flangePlateWeldL22.place(flangePlateWeldL22Origin, flangePlateWeldL22_uDir, flangePlateWeldL22_wDir) - - self.flangePlateWeldL22Model = self.flangePlateWeldL22.create_model() - - flangePlateWeldW21Origin = numpy.array([self.flangePlate.L / 2, self.flangePlate.W / 2, -(self.beam.D) / 2]) - flangePlateWeldW21_uDir = numpy.array([0.0, 0.0, -1.0]) - flangePlateWeldW21_wDir = numpy.array([0.0, -1.0, 0.0]) - self.flangePlateWeldW21.place(flangePlateWeldW21Origin, flangePlateWeldW21_uDir, flangePlateWeldW21_wDir) - - self.flangePlateWeldW21Model = self.flangePlateWeldW21.create_model() - - flangePlateWeldW22Origin = numpy.array([-self.flangePlate.L / 2, -self.flangePlate.W / 2, -(self.beam.D) / 2]) - flangePlateWeldW22_uDir = numpy.array([0.0, 0.0, -1.0]) - flangePlateWeldW22_wDir = numpy.array([0.0, 1.0, 0.0]) - self.flangePlateWeldW22.place(flangePlateWeldW22Origin, flangePlateWeldW22_uDir, flangePlateWeldW22_wDir) - - self.flangePlateWeldW22Model = self.flangePlateWeldW22.create_model() - - # Webplate1 (right side) - webPlateWeldL11Origin = numpy.array([-self.webPlateWeldL.L / 2, (self.beam.t) / 2, self.webPlate.W / 2]) - webPlateWeldL11_uDir = numpy.array([0.0, 1.0, 0.0]) - webPlateWeldL11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.webPlateWeldL11.place(webPlateWeldL11Origin, webPlateWeldL11_uDir, webPlateWeldL11_wDir) - - self.webPlateWeldL11Model = self.webPlateWeldL11.create_model() - - webPlateWeldL12Origin = numpy.array([self.webPlateWeldL.L / 2, (self.beam.t) / 2, -self.webPlate.W / 2]) - webPlateWeldL12_uDir = numpy.array([0.0, 1.0, 0.0]) - webPlateWeldL12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.webPlateWeldL12.place(webPlateWeldL12Origin, webPlateWeldL12_uDir, webPlateWeldL12_wDir) - - self.webPlateWeldL12Model = self.webPlateWeldL12.create_model() - - webPlateWeldW11Origin = numpy.array([self.webPlate.L / 2, (self.beam.t) / 2, self.webPlate.W / 2]) - webPlateWeldW11_uDir = numpy.array([0.0, 1.0, 0.0]) - webPlateWeldW11_wDir = numpy.array([0.0, 0.0, -1.0]) - self.webPlateWeldW11.place(webPlateWeldW11Origin, webPlateWeldW11_uDir, webPlateWeldW11_wDir) - - self.webPlateWeldW11Model = self.webPlateWeldW11.create_model() - - webPlateWeldW12Origin = numpy.array([-self.webPlate.L / 2, (self.beam.t) / 2, -self.webPlate.W / 2]) - webPlateWeldW12_uDir = numpy.array([0.0, 1.0, 0.0]) - webPlateWeldW12_wDir = numpy.array([0.0, 0.0, 1.0]) - self.webPlateWeldW12.place(webPlateWeldW12Origin, webPlateWeldW12_uDir, webPlateWeldW12_wDir) - - self.webPlateWeldW12Model = self.webPlateWeldW12.create_model() - - # Webplate2 - webPlateWeldL21Origin = numpy.array([self.webPlateWeldL.L / 2, -(self.beam.t) / 2, self.webPlate.W / 2]) - webPlateWeldL21_uDir = numpy.array([0.0, -1.0, 0.0]) - webPlateWeldL21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.webPlateWeldL21.place(webPlateWeldL21Origin, webPlateWeldL21_uDir, webPlateWeldL21_wDir) - - self.webPlateWeldL21Model = self.webPlateWeldL21.create_model() - - webPlateWeldL22Origin = numpy.array([-self.webPlateWeldL.L / 2, -(self.beam.t) / 2, -self.webPlate.W / 2]) - webPlateWeldL22_uDir = numpy.array([0.0, -1.0, 0.0]) - webPlateWeldL22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.webPlateWeldL22.place(webPlateWeldL22Origin, webPlateWeldL22_uDir, webPlateWeldL22_wDir) - - self.webPlateWeldL22Model = self.webPlateWeldL22.create_model() - - webPlateWeldW21Origin = numpy.array([self.webPlate.L / 2, -(self.beam.t) / 2, -self.webPlate.W / 2]) - webPlateWeldW21_uDir = numpy.array([0.0, -1.0, 0.0]) - webPlateWeldW21_wDir = numpy.array([0.0, 0.0, 1.0]) - self.webPlateWeldW21.place(webPlateWeldW21Origin, webPlateWeldW21_uDir, webPlateWeldW21_wDir) - - self.webPlateWeldW21Model = self.webPlateWeldW21.create_model() - - webPlateWeldW22Origin = numpy.array([-self.webPlate.L / 2, -(self.beam.t) / 2, self.webPlate.W / 2]) - webPlateWeldW22_uDir = numpy.array([0.0, -1.0, 0.0]) - webPlateWeldW22_wDir = numpy.array([0.0, 0.0, -1.0]) - self.webPlateWeldW22.place(webPlateWeldW22Origin, webPlateWeldW22_uDir, webPlateWeldW22_wDir) - - self.webPlateWeldW22Model = self.webPlateWeldW22.create_model() - - # Todo: Add if statement for inner plate condition - if self.B.preference != 'Outside': - # innerplate1 (right top) - innerFlangePlatespacing = self.flangespace + self.beam.t / 2 + self.beam.R1 - innerflangePlateWeldL11Origin = numpy.array( - [self.innerFlangePlate.L / 2, innerFlangePlatespacing + self.innerFlangePlate.W, - (self.beam.D) / 2 - self.beam.T]) - innerflangePlateWeldL11_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldL11_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerflangePlateWeldL11.place(innerflangePlateWeldL11Origin, innerflangePlateWeldL11_uDir, - innerflangePlateWeldL11_wDir) - - self.innerflangePlateWeldL11Model = self.innerflangePlateWeldL11.create_model() - - innerflangePlateWeldL12Origin = numpy.array( - [-self.innerFlangePlate.L / 2, innerFlangePlatespacing, (self.beam.D) / 2 - self.beam.T]) - innerflangePlateWeldL12_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldL12_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerflangePlateWeldL12.place(innerflangePlateWeldL12Origin, innerflangePlateWeldL12_uDir, - innerflangePlateWeldL12_wDir) - - self.innerflangePlateWeldL12Model = self.innerflangePlateWeldL12.create_model() - - innerflangePlateWeldW11Origin = numpy.array( - [self.innerFlangePlate.L / 2, innerFlangePlatespacing + self.innerFlangePlate.W, - (self.beam.D) / 2 - self.beam.T]) - innerflangePlateWeldW11_uDir = numpy.array([0.0, 0.0, -1.0]) - innerflangePlateWeldW11_wDir = numpy.array([0.0, -1.0, 0.0]) - self.innerflangePlateWeldW11.place(innerflangePlateWeldW11Origin, innerflangePlateWeldW11_uDir, - innerflangePlateWeldW11_wDir) - - self.innerflangePlateWeldW11Model = self.innerflangePlateWeldW11.create_model() - - innerflangePlateWeldW12Origin = numpy.array( - [-self.innerFlangePlate.L / 2, innerFlangePlatespacing, (self.beam.D) / 2 - self.beam.T]) - innerflangePlateWeldW12_uDir = numpy.array([0.0, 0.0, -1.0]) - innerflangePlateWeldW12_wDir = numpy.array([0.0, 1.0, 0.0]) - self.innerflangePlateWeldW12.place(innerflangePlateWeldW12Origin, innerflangePlateWeldW12_uDir, - innerflangePlateWeldW12_wDir) - - self.innerflangePlateWeldW12Model = self.innerflangePlateWeldW12.create_model() - - # innerplate2 (top left) - innerflangePlateWeldL21Origin = numpy.array( - [self.innerFlangePlate.L / 2, -innerFlangePlatespacing, (self.beam.D) / 2 - self.beam.T]) - innerflangePlateWeldL21_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldL21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerflangePlateWeldL21.place(innerflangePlateWeldL21Origin, innerflangePlateWeldL21_uDir, - innerflangePlateWeldL21_wDir) - - self.innerflangePlateWeldL21Model = self.innerflangePlateWeldL21.create_model() - - innerflangePlateWeldL22Origin = numpy.array( - [-self.innerFlangePlate.L / 2, -innerFlangePlatespacing - self.innerFlangePlate.W, - (self.beam.D) / 2 - self.beam.T]) - innerflangePlateWeldL22_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldL22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerflangePlateWeldL22.place(innerflangePlateWeldL22Origin, innerflangePlateWeldL22_uDir, - innerflangePlateWeldL22_wDir) - - self.innerflangePlateWeldL22Model = self.innerflangePlateWeldL22.create_model() - - innerflangePlateWeldW21Origin = numpy.array( - [self.innerFlangePlate.L / 2, - innerFlangePlatespacing, (self.beam.D) / 2 - self.beam.T]) - innerflangePlateWeldW21_uDir = numpy.array([0.0, 0.0, -1.0]) - innerflangePlateWeldW21_wDir = numpy.array([0.0, -1.0, 0.0]) - self.innerflangePlateWeldW21.place(innerflangePlateWeldW21Origin, innerflangePlateWeldW21_uDir, - innerflangePlateWeldW21_wDir) - - self.innerflangePlateWeldW21Model = self.innerflangePlateWeldW21.create_model() - - innerflangePlateWeldW22Origin = numpy.array( - [-self.innerFlangePlate.L / 2, -innerFlangePlatespacing - self.innerFlangePlate.W, - (self.beam.D) / 2 - self.beam.T]) - innerflangePlateWeldW22_uDir = numpy.array([0.0, 0.0, -1.0]) - innerflangePlateWeldW22_wDir = numpy.array([0.0, 1.0, 0.0]) - self.innerflangePlateWeldW22.place(innerflangePlateWeldW22Origin, innerflangePlateWeldW22_uDir, - innerflangePlateWeldW22_wDir) - - self.innerflangePlateWeldW22Model = self.innerflangePlateWeldW22.create_model() - - # innerplate3 (Right bottom) - innerflangePlateWeldL31Origin = numpy.array( - [-self.innerFlangePlate.L / 2, innerFlangePlatespacing + self.innerFlangePlate.W, - -(self.beam.D) / 2 + self.beam.T]) - innerflangePlateWeldL31_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldL31_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerflangePlateWeldL31.place(innerflangePlateWeldL31Origin, innerflangePlateWeldL31_uDir, - innerflangePlateWeldL31_wDir) - - self.innerflangePlateWeldL31Model = self.innerflangePlateWeldL31.create_model() - - innerflangePlateWeldL32Origin = numpy.array( - [self.innerFlangePlate.L / 2, innerFlangePlatespacing, -(self.beam.D) / 2 + self.beam.T]) - innerflangePlateWeldL32_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldL32_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerflangePlateWeldL32.place(innerflangePlateWeldL32Origin, innerflangePlateWeldL32_uDir, - innerflangePlateWeldL32_wDir) - - self.innerflangePlateWeldL32Model = self.innerflangePlateWeldL32.create_model() - - innerflangePlateWeldW31Origin = numpy.array( - [self.innerFlangePlate.L / 2, innerFlangePlatespacing, -(self.beam.D) / 2 + self.beam.T]) - innerflangePlateWeldW31_uDir = numpy.array([0.0, 0.0, 1.0]) - innerflangePlateWeldW31_wDir = numpy.array([0.0, 1.0, 0.0]) - self.innerflangePlateWeldW31.place(innerflangePlateWeldW31Origin, innerflangePlateWeldW31_uDir, - innerflangePlateWeldW31_wDir) - - self.innerflangePlateWeldW31Model = self.innerflangePlateWeldW31.create_model() - - innerflangePlateWeldW32Origin = numpy.array( - [-self.innerFlangePlate.L / 2, innerFlangePlatespacing + self.innerFlangePlate.W, - -(self.beam.D) / 2 + self.beam.T]) - innerflangePlateWeldW32_uDir = numpy.array([0.0, 0.0, 1.0]) - innerflangePlateWeldW32_wDir = numpy.array([0.0, -1.0, 0.0]) - self.innerflangePlateWeldW32.place(innerflangePlateWeldW32Origin, innerflangePlateWeldW32_uDir, - innerflangePlateWeldW32_wDir) - - self.innerflangePlateWeldW32Model = self.innerflangePlateWeldW32.create_model() - - # innetplate4 (left bottom) - innerflangePlateWeldL41Origin = numpy.array( - [-self.innerFlangePlate.L / 2, -innerFlangePlatespacing, -(self.beam.D) / 2 + self.beam.T]) - innerflangePlateWeldL41_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldL41_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerflangePlateWeldL41.place(innerflangePlateWeldL41Origin, innerflangePlateWeldL41_uDir, - innerflangePlateWeldL41_wDir) - - self.innerflangePlateWeldL41Model = self.innerflangePlateWeldL41.create_model() - - innerflangePlateWeldL42Origin = numpy.array( - [self.innerFlangePlate.L / 2, -innerFlangePlatespacing - self.innerFlangePlate.W, - -(self.beam.D) / 2 + self.beam.T]) - innerflangePlateWeldL42_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldL42_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerflangePlateWeldL42.place(innerflangePlateWeldL42Origin, innerflangePlateWeldL42_uDir, - innerflangePlateWeldL42_wDir) - - self.innerflangePlateWeldL42Model = self.innerflangePlateWeldL42.create_model() - - innerflangePlateWeldW41Origin = numpy.array( - [self.innerFlangePlate.L / 2, -innerFlangePlatespacing - self.innerFlangePlate.W, - -(self.beam.D) / 2 + self.beam.T]) - innerflangePlateWeldW41_uDir = numpy.array([0.0, 0.0, 1.0]) - innerflangePlateWeldW41_wDir = numpy.array([0.0, 1.0, 0.0]) - self.innerflangePlateWeldW41.place(innerflangePlateWeldW41Origin, innerflangePlateWeldW41_uDir, - innerflangePlateWeldW41_wDir) - - self.innerflangePlateWeldW41Model = self.innerflangePlateWeldW41.create_model() - - innerflangePlateWeldW42Origin = numpy.array( - [-self.innerFlangePlate.L / 2, -innerFlangePlatespacing, -(self.beam.D) / 2 + self.beam.T]) - innerflangePlateWeldW42_uDir = numpy.array([0.0, 0.0, 1.0]) - innerflangePlateWeldW42_wDir = numpy.array([0.0, -1.0, 0.0]) - self.innerflangePlateWeldW42.place(innerflangePlateWeldW42Origin, innerflangePlateWeldW42_uDir, - innerflangePlateWeldW42_wDir) - - self.innerflangePlateWeldW42Model = self.innerflangePlateWeldW42.create_model() - - # to cut the welds - weldCutPlateOrigin = numpy.array([0.0, -self.weldCutPlate.W / 2, 0.0]) - weldCutPlate_uDir = numpy.array([1.0, 0.0, 0.0]) - weldCutPlate_wDir = numpy.array([0.0, 1.0, 0.0]) - self.weldCutPlate.place(weldCutPlateOrigin, weldCutPlate_uDir, weldCutPlate_wDir) - - self.weldCutPlateModel = self.weldCutPlate.create_model() - - def get_beam_models(self): - """ - - :return: CAD mode for the beams - """ - beams = BRepAlgoAPI_Fuse(self.beam1Model, self.beam2Model).Shape() - - return beams - - def get_plate_models(self): - """ - :return: CAD model for all the plates - """ - - # Todo: Ad an ifelse statement for outer and inner plates - if self.B.preference != 'Outside': - plates_sec = [self.flangePlate1Model, self.flangePlate2Model, self.innerFlangePlate1Model, - self.innerFlangePlate2Model, self.innerFlangePlate3Model, self.innerFlangePlate4Model, - self.webPlate1Model, self.webPlate2Model] - else: - plates_sec = [self.flangePlate1Model, self.flangePlate2Model, self.webPlate1Model, self.webPlate2Model] - plates = plates_sec[0] - - for comp in plates_sec[1:]: - plates = BRepAlgoAPI_Fuse(comp, plates).Shape() - - return plates - - def get_welded_modules(self): - """ - :return: CAD model for all the welds - """ - # Todo: Add an ifelse statement for outer and inner plate wleds - if self.B.preference != 'Outside': - welded_sec = [self.flangePlateWeldL11Model, self.flangePlateWeldL12Model, self.flangePlateWeldL21Model, - self.flangePlateWeldL22Model, self.flangePlateWeldW11Model, self.flangePlateWeldW12Model, - self.flangePlateWeldW21Model, self.flangePlateWeldW22Model, \ - self.webPlateWeldL11Model, self.webPlateWeldL12Model, self.webPlateWeldL21Model, - self.webPlateWeldL22Model, self.webPlateWeldW11Model, self.webPlateWeldW12Model, - self.webPlateWeldW21Model, self.webPlateWeldW22Model, \ - self.innerflangePlateWeldL11Model, self.innerflangePlateWeldL12Model, - self.innerflangePlateWeldL21Model, self.innerflangePlateWeldL22Model, - self.innerflangePlateWeldL31Model, self.innerflangePlateWeldL32Model, - self.innerflangePlateWeldL41Model, self.innerflangePlateWeldL42Model, \ - self.innerflangePlateWeldW11Model, self.innerflangePlateWeldW12Model, - self.innerflangePlateWeldW21Model, self.innerflangePlateWeldW22Model, - self.innerflangePlateWeldW31Model, self.innerflangePlateWeldW32Model, - self.innerflangePlateWeldW41Model, self.innerflangePlateWeldW42Model] - else: - welded_sec = [self.flangePlateWeldL11Model, self.flangePlateWeldL12Model, self.flangePlateWeldL21Model, - self.flangePlateWeldL22Model, self.flangePlateWeldW11Model, self.flangePlateWeldW12Model, - self.flangePlateWeldW21Model, self.flangePlateWeldW22Model, \ - self.webPlateWeldL11Model, self.webPlateWeldL12Model, self.webPlateWeldL21Model, - self.webPlateWeldL22Model, self.webPlateWeldW11Model, self.webPlateWeldW12Model, - self.webPlateWeldW21Model, self.webPlateWeldW22Model] - - welds = welded_sec[0] - - for comp in welded_sec[1:]: - welds = BRepAlgoAPI_Fuse(comp, welds).Shape() - - welds = BRepAlgoAPI_Cut(welds, self.weldCutPlateModel).Shape() - - return welds - # return self.webPlateWeldW22Model - - def get_models(self): - beams = self.get_beam_models() - plate_conectors = self.get_plate_models() - welds = self.get_welded_modules() - - CAD = BRepAlgoAPI_Fuse(beams, plate_conectors).Shape() - CAD = BRepAlgoAPI_Fuse(CAD, welds).Shape() - - return CAD - -if __name__ == '__main__': - from cad.items.ISection import ISection - from cad.items.plate import Plate - from cad.items.filletweld import FilletWeld - - import OCC.Core.V3d - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - beam = ISection(B=250, T=13.5, D=450, t=9.8, R1=15, R2=75, alpha=94, length=1000, notchObj=None) - flangePlate = Plate(L=550, W=210, T=14) - innerFlangePlate = Plate(L=550, W=80, T=14) - webPlate = Plate(L=365, W=170, T=8) - gap = 10 - - flangePlateWeldL = FilletWeld(h=5, b=5, L=flangePlate.L) - flangePlateWeldW = FilletWeld(h=5, b=5, L=flangePlate.W) - - innerflangePlateWeldL = FilletWeld(h=5, b=5, L=innerFlangePlate.L) - innerflangePlateWeldW = FilletWeld(h=5, b=5, L=innerFlangePlate.W) - - webPlateWeldL = FilletWeld(h=5, b=5, L=webPlate.L) - webPlateWeldW = FilletWeld(h=5, b=5, L=webPlate.W) - - BBSpliceCoverPlateCAD = BBSpliceCoverPlateWeldedCAD(beam, flangePlate, innerFlangePlate, webPlate, gap, - flangePlateWeldL, flangePlateWeldW, innerflangePlateWeldL, - innerflangePlateWeldW, webPlateWeldL, webPlateWeldW) - - BBSpliceCoverPlateCAD.create_3DModel() - beam = BBSpliceCoverPlateCAD.get_beam_models() - plates = BBSpliceCoverPlateCAD.get_plate_models() - welds = BBSpliceCoverPlateCAD.get_welded_modules() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - # display.View.Rotate(45, 90, 45) - display.DisplayShape(beam, update=True) - display.DisplayShape(plates, color='BLUE', update=True) - display.DisplayShape(welds, color='RED', update=True) - - display.DisableAntiAliasing() - start_display() diff --git a/cad/MomentConnections/CCEndPlateCAD/nutBoltPlacement.py b/cad/MomentConnections/CCEndPlateCAD/nutBoltPlacement.py deleted file mode 100644 index ff2189b82..000000000 --- a/cad/MomentConnections/CCEndPlateCAD/nutBoltPlacement.py +++ /dev/null @@ -1,384 +0,0 @@ -""" -created on 02-06-2020 -@auther: Anand Swaroop -""" - -import numpy as np -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse -from cad.items.ModelUtils import getGpPt -from cad.items.bolt import Bolt -from cad.items.nut import Nut - - -class NutBoltArray(object): - def __init__(self, Obj, column, nut, bolt, nut_space): - - self.Obj = Obj - self.column = column - self.nut = nut - self.bolt = bolt - self.gap = nut_space - - self.origin = None - self.gaugeDir = None - self.pitchDir = None - self.boltDir = None - - self.initBoltPlaceParams(Obj) - - self.bolts = [] - self.nuts = [] - self.initialiseNutBolts() - - self.positions = [] - - self.models = [] - - def initialiseNutBolts(self): - """ - Initialise the Nut and Bolt - """ - b = self.bolt - n = self.nut - for i in range(self.numOfBolts): - bolt_length_required = float(b.T + self.gap) - b.H = bolt_length_required + (bolt_length_required - 5) % 5 - self.bolts.append(Bolt(b.R, b.T, b.H, b.r)) - self.nuts.append(Nut(n.R, n.T, n.H, n.r1)) - - def initBoltPlaceParams(self, Obj): - self.row = int(Obj.n_bw) # int(Obj.n_bw) # 4 # #4 - self.col = int(Obj.n_bf) * 2 # 2 # int(Obj.n_bf * 2) #4 # #4 - self.webcol = 2 - self.numOfBolts = Obj.no_bolts # 12 # - self.endDist = Obj.end_dist - - self.pitch = Obj.pitch - self.p2flange = Obj.p_2_flange - self.p2web = Obj.p_2_web - # self.webColgauge = 2 * self.endDist + self.column.t - self.edgeDist = self.column.B / 2 - self.endDist - self.column.t / 2 - # todo for flush plate - if Obj.connection == "Flush End Plate": - if self.row == 2: - self.pitchDist = [self.endDist + self.column.T, self.p2web] - elif self.row == 3: - self.pitchDist = [self.endDist + self.column.T, self.p2web, self.p2web] - elif self.row == 4: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.p2web, self.pitch] - elif self.row == 5: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.p2web, self.p2web, self.pitch] - - elif self.row == 6: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.pitch, self.p2web, self.pitch, self.pitch] - elif self.row == 7: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.pitch, self.p2web, self.p2web, self.pitch, self.pitch] - elif self.row == 8: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.pitch, self.pitch, self.p2web, self.pitch, self.pitch, self.pitch] - elif self.row == 9: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.pitch, self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch] - elif self.row == 10: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.pitch, self.pitch, self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 11: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.pitch, self.pitch, self.pitch,self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 12: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 13: - self.pitchDist = [self.endDist + self.column.T, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 14: - self.pitchDist = [self.endDist + self.column.T, self.pitch, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 15: - self.pitchDist = [self.endDist + self.column.T, self.pitch, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 16: - self.pitchDist = [self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 17: - self.pitchDist = [self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 18: - self.pitchDist = [self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 19: - self.pitchDist = [self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch] - - elif self.row == 20: - self.pitchDist = [self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.p2web, self.pitch, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch] - - - elif Obj.connection == "Extended Both Ways": - self.row = self.row + 2 - if self.row == 4: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.p2web, - 2 * self.endDist + self.column.T] - elif self.row == 5: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.p2web, self.p2web, - 2 * self.endDist + self.column.T] - elif self.row == 6: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, - self.p2web, self.pitch, 2 * self.endDist + self.column.T] - elif self.row == 7: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, - self.p2web, self.p2web, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 8: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, - self.pitch, self.p2web, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - elif self.row == 9: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, - self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 10: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, - self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 11: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, - self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 12: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, - self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 13: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, - self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 14: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 15: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 16: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 17: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 18: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 19: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.pitch,self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch,self.pitch, self.pitch, - self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 20: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 21: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.pitch,self.pitch, self.p2web, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch,self.pitch, self.pitch, - self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - elif self.row == 22: - self.pitchDist = [self.endDist, 2 * self.endDist + self.column.T, self.pitch, self.pitch, self.pitch, - self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.p2web, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, self.pitch, - self.pitch, self.pitch, 2 * self.endDist + self.column.T] - - if self.col == 2: - self.gauge = [self.edgeDist, 2 * self.endDist + self.column.t] # end+T+end - - elif self.col == 4: - self.gauge = [self.endDist, self.p2flange, 2 * self.endDist + self.column.t, self.p2flange] - elif self.col == 6: - self.gauge = [self.endDist, self.p2flange, self.p2flange, 2 * self.endDist + self.column.t, self.p2flange, - self.p2flange] - - elif self.col == 8: - self.gauge = [self.endDist, self.pitch, self.p2flange, self.pitch, 2 * self.endDist + self.column.t, - self.pitch, self.p2flange, self.pitch] - - elif self.col == 10: - self.gauge = [self.endDist, self.pitch, self.p2flange, self.p2flange, self.pitch, - self.column.t + 2 * self.endDist, self.pitch, self.p2flange, self.p2flange, self.pitch] - - elif self.col == 12: - self.gauge = [self.endDist, self.pitch, self.pitch, self.p2flange, self.pitch, self.pitch, 2 * self.endDist + self.column.t, - self.pitch, self.pitch, self.p2flange, self.pitch, self.pitch] - - elif self.col == 14: - self.gauge = [self.endDist, self.pitch, self.pitch, self.p2flange, self.p2flange, self.pitch, self.pitch, 2 * self.endDist + self.column.t, - self.pitch, self.pitch, self.p2flange, self.p2flange, self.pitch, self.pitch] - - elif self.col == 16: - self.gauge = [self.endDist, self.pitch, self.pitch, self.pitch, self.p2flange, self.pitch, self.pitch, self.pitch, 2 * self.endDist + self.column.t, - self.pitch, self.pitch, self.pitch, self.p2flange, self.pitch, self.pitch, self.pitch] - - def calculatePositions(self): - self.positions = [] - - # Todo: if member == flush: - self.boltOrigin = self.origin # + (self.edgeDist + self.column.t) * self.gaugeDir - # self.boltOrigin = self.boltOrigin # + self.endDist * self.pitchDir - xrow = 0.0 - for rw in range(self.row): - xcol = 0.0 - xrow = xrow.__add__(self.pitchDist[rw]) - if self.Obj.connection == "Flush End Plate": - if rw == 0 or rw == self.row - 1: - for col in range(self.col): - xcol = xcol.__add__(self.gauge[col]) - pos = self.boltOrigin - pos = pos + xrow * self.pitchDir - pos = pos + xcol * self.gaugeDir - - self.positions.append(pos) - - else: - for col in range(self.webcol): - # xcol = xcol.__add__(self.gauge[col]) - pos = self.boltOrigin + self.edgeDist * self.gaugeDir - pos = pos + xrow * self.pitchDir - pos = pos + col * (2 * self.endDist + self.column.t) * self.gaugeDir - - self.positions.append(pos) - - else: - if rw == 0 or rw == 1 or rw == self.row - 1 or rw == self.row - 2: - for col in range(self.col): - xcol = xcol.__add__(self.gauge[col]) - pos = self.boltOrigin - pos = pos + xrow * self.pitchDir - pos = pos + xcol * self.gaugeDir - - self.positions.append(pos) - - else: - for col in range(self.webcol): - # xcol = xcol.__add__(self.gauge[col]) - pos = self.boltOrigin + self.edgeDist * self.gaugeDir - pos = pos + xrow * self.pitchDir - pos = pos + col * (2 * self.endDist + self.column.t) * self.gaugeDir - - self.positions.append(pos) - - def place(self, origin, gaugeDir, pitchDir, boltDir): - """ - :param origin: Origin for bolt placement - :param gaugeDir: gauge distance direction - :param pitchDir: pitch distance direction - :param boltDir: bolts screwing direction - :return: - """ - - self.origin = origin - self.gaugeDir = gaugeDir - self.pitchDir = pitchDir - self.boltDir = boltDir - - self.calculatePositions() - - for index, pos in enumerate(self.positions): - self.bolts[index].place(pos, gaugeDir, boltDir) - self.nuts[index].place((pos + self.gap * boltDir), gaugeDir, - -boltDir) # gap here is between bolt head and nut - - def create_model(self): - """ - - :return: cad model of nut bolt arrangement - """ - for bolt in self.bolts: - self.models.append(bolt.create_model()) - - for nut in self.nuts: - self.models.append(nut.create_model()) - - dbg = self.dbgSphere(self.origin) # TODO : know why sphere is appended to the model (by Anand Swaroop) - self.models.append(dbg) - - nut_bolts = self.models - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - - return array - - def dbgSphere(self, pt): - """ - TODO : know why sphere is appended to the model, if no reason than remove sphere from all the cad files (by Anand Swaroop) - :param pt: pt of origin for the nut bol placement - :return: returns the sphere - """ - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_models(self): - """ - - :return: cad model for nut and bolt arrangement - """ - nut_bolts = self.models - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - - return array - - -if __name__ == '__main__': - from cad.items.bolt import Bolt - from cad.items.nut import Nut - import numpy - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - nutboltArrayOrigin = numpy.array([0., 0., 0.]) - gaugeDir = numpy.array([0.0, 1.0, 0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 0, 1.0]) - - bolt = Bolt(R=6, T=5, H=6, r=3) - nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - nut_space = 10 + 5 + nut.T # member.T + plate.T + nut.T - Obj = '6' - - nut_bolt_array = NutBoltArray(Obj, nut, bolt, nut_space) - - nut_bolt_array.place(nutboltArrayOrigin, pitchDir, gaugeDir, boltDir) - nut_bolt_array.create_model() - - array = nut_bolt_array.get_models() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(array, color='YELLOW', update=True) - # display.DisplayShape(parray, color= 'BLUE', update=True) - display.DisableAntiAliasing() - start_display() diff --git a/cad/MomentConnections/CCSpliceCoverPlateCAD/BoltedCAD.py b/cad/MomentConnections/CCSpliceCoverPlateCAD/BoltedCAD.py deleted file mode 100644 index 55987600e..000000000 --- a/cad/MomentConnections/CCSpliceCoverPlateCAD/BoltedCAD.py +++ /dev/null @@ -1,280 +0,0 @@ -""" -created on 14-04-2020 - -""" - -import numpy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut -import copy - -class CCSpliceCoverPlateBoltedCAD(object): - def __init__(self, C, column, flangePlate, innerFlangePlate, webPlate, nut_bolt_array_AF, nut_bolt_array_BF, nut_bolt_array_Web): - - self.C = C - self.column = column - self.flangePlate = flangePlate - self.innerFlangePlate = innerFlangePlate - self.webPlate = webPlate - self.nut_bolt_array_AF = nut_bolt_array_AF - self.nut_bolt_array_BF = nut_bolt_array_BF - self.nut_bolt_array_Web = nut_bolt_array_Web - - self.gap = float(self.C.flange_plate.gap) - - - self.column1 = copy.deepcopy(self.column) - self.column2 = copy.deepcopy(self.column) - - self.flangePlate1 = copy.deepcopy(self.flangePlate) - self.flangePlate2 = copy.deepcopy(self.flangePlate) - - self.innerFlangePlate1 = copy.deepcopy(self.innerFlangePlate) - self.innerFlangePlate2 = copy.deepcopy(self.innerFlangePlate) - self.innerFlangePlate3 = copy.deepcopy(self.innerFlangePlate) - self.innerFlangePlate4 = copy.deepcopy(self.innerFlangePlate) - - self.webPlate1 = copy.deepcopy(self.webPlate) - self.webPlate2 = copy.deepcopy(self.webPlate) - - def create_3DModel(self): - ''' - :return: CAD model of each of the followings. Debugging each command below would give give clear picture - ''' - - self.createColumnGeometry() - self.createPlateGeometry() - self.create_nut_bolt_array() - - def createColumnGeometry(self): - """ - - :return: Geometric Orientation of this component - """ - column1Origin = numpy.array([0.0, 0.0, self.gap/2]) - column1_uDir = numpy.array([1.0, 0.0, 0.0]) - column1_wDir = numpy.array([0.0, 0.0, 1.0]) - self.column1.place(column1Origin, column1_uDir, column1_wDir) - - self.column1Model = self.column1.create_model() - - column2Origin = numpy.array([0.0, 0.0, -self.gap/2]) - column2_uDir = numpy.array([1.0, 0.0, 0.0]) - column2_wDir = numpy.array([0.0, 0.0, -1.0]) - self.column2.place(column2Origin, column2_uDir, column2_wDir) - - self.column2Model = self.column2.create_model() - - def createPlateGeometry(self): - flangePlate1Origin = numpy.array([-self.flangePlate.W/2, (self.column.D + self.flangePlate.T)/2, 0.0]) - flangePlate1_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlate1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.flangePlate1.place(flangePlate1Origin, flangePlate1_uDir, flangePlate1_wDir) - - self.flangePlate1Model = self.flangePlate1.create_model() - - flangePlate2Origin = numpy.array([-self.flangePlate.W/2, -(self.column.D + self.flangePlate.T)/2, 0.0]) - flangePlate2_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlate2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.flangePlate2.place(flangePlate2Origin, flangePlate2_uDir, flangePlate2_wDir) - - self.flangePlate2Model = self.flangePlate2.create_model() - - webPlate1Origin = numpy.array([(self.column.t + self.webPlate.T)/2, -self.webPlate.W/2, 0.0]) - webPlate1_uDir = numpy.array([1.0, 0.0, 0.0]) - webPlate1_wDir = numpy.array([0.0, 1.0, 0.0]) - self.webPlate1.place(webPlate1Origin, webPlate1_uDir, webPlate1_wDir) - - self.webPlate1Model = self.webPlate1.create_model() - - webPlate2Origin = numpy.array([-(self.column.t + self.webPlate.T)/2, -self.webPlate.W/2, 0.0]) - webPlate2_uDir = numpy.array([1.0, 0.0, 0.0]) - webPlate2_wDir = numpy.array([0.0, 1.0, 0.0]) - self.webPlate2.place(webPlate2Origin, webPlate2_uDir, webPlate2_wDir) - - self.webPlate2Model = self.webPlate2.create_model() - - if self.C.preference != 'Outside': - innerFlangePlatespacing = self.column.B/2 - self.innerFlangePlate.W - innerFlangePlate1Origin = numpy.array([innerFlangePlatespacing, (self.column.D - self.innerFlangePlate.T)/2 - self.column.T, 0.0]) - innerFlangePlate1_uDir = numpy.array([0.0, 1.0, 0.0]) - innerFlangePlate1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerFlangePlate1.place(innerFlangePlate1Origin, innerFlangePlate1_uDir, innerFlangePlate1_wDir) - - self.innerFlangePlate1Model = self.innerFlangePlate1.create_model() - - innerFlangePlate2Origin = numpy.array([innerFlangePlatespacing, -(self.column.D - self.innerFlangePlate.T)/2 + self.column.T, 0.0]) - innerFlangePlate2_uDir = numpy.array([0.0, 1.0, 0.0]) - innerFlangePlate2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerFlangePlate2.place(innerFlangePlate2Origin, innerFlangePlate2_uDir, innerFlangePlate2_wDir) - - self.innerFlangePlate2Model = self.innerFlangePlate2.create_model() - - innerFlangePlate3Origin = numpy.array([-innerFlangePlatespacing, -(self.column.D --- self.innerFlangePlate.T)/2 + self.column.T, 0.0]) - innerFlangePlate3_uDir = numpy.array([0.0, 1.0, 0.0]) - innerFlangePlate3_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerFlangePlate3.place(innerFlangePlate3Origin, innerFlangePlate3_uDir, innerFlangePlate3_wDir) - - self.innerFlangePlate3Model = self.innerFlangePlate3.create_model() - - innerFlangePlate4Origin = numpy.array([-innerFlangePlatespacing, (self.column.D - self.innerFlangePlate.T)/2 - self.column.T, 0.0]) - innerFlangePlate4_uDir = numpy.array([0.0, 1.0, 0.0]) - innerFlangePlate4_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerFlangePlate4.place(innerFlangePlate4Origin, innerFlangePlate4_uDir, innerFlangePlate4_wDir) - - self.innerFlangePlate4Model = self.innerFlangePlate4.create_model() - - - def create_nut_bolt_array(self): - - nutBoltOriginAF = self.flangePlate1.sec_origin + numpy.array([0.0, self.flangePlate.T/2, -self.flangePlate.L/2]) - - gaugeDirAF = numpy.array([1.0, 0, 0]) - pitchDirAF = numpy.array([0, 0.0, 1.0]) - boltDirAF = numpy.array([0, -1.0, 0.0]) - width = self.column.B - self.nut_bolt_array_AF.placeAF(nutBoltOriginAF, gaugeDirAF, pitchDirAF, boltDirAF, width) - self.nut_bolt_array_AF.create_modelAF() - - nutBoltOriginBF = self.flangePlate2.sec_origin + numpy.array([0.0, -self.flangePlate.T/2, -self.flangePlate.L/2]) - - gaugeDirBF = numpy.array([1.0, 0, 0]) - pitchDirBF = numpy.array([0, 0.0, 1.0]) - boltDirBF = numpy.array([0, 1.0, 0.0]) - width = self.column.B - self.nut_bolt_array_BF.placeBF(nutBoltOriginBF, gaugeDirBF, pitchDirBF, boltDirBF, width) - self.nut_bolt_array_BF.create_modelBF() - - boltWeb_X = self.webPlate.T / 2 - boltWeb_Y = self.webPlate.W - nutBoltOriginW = self.webPlate1.sec_origin + numpy.array([boltWeb_X, boltWeb_Y, -self.webPlate.L/2]) - gaugeDirW = numpy.array([0, 0.0, 1.0]) - pitchDirW = numpy.array([0, -1.0, .0]) - boltDirW = numpy.array([-1.0, 0, 0]) - self.nut_bolt_array_Web.placeW(nutBoltOriginW, gaugeDirW, pitchDirW, boltDirW) - self.nut_bolt_array_Web.create_modelW() - - return nutBoltOriginAF - - - def get_column_models(self): - """ - - :return: CAD mode for the columns - """ - columns = BRepAlgoAPI_Fuse(self.column1Model, self.column2Model).Shape() - - return columns - - def get_plate_models(self): - """ - :return: CAD model for all the plates - """ - if self.C.preference != 'Outside': - plates_sec = [self.flangePlate1Model, self.flangePlate2Model, self.innerFlangePlate1Model, - self.innerFlangePlate2Model, self.innerFlangePlate3Model, self.innerFlangePlate4Model, - self.webPlate1Model, self.webPlate2Model] - else: - plates_sec = [self.flangePlate1Model, self.flangePlate2Model, - self.webPlate1Model, self.webPlate2Model] - - plates = plates_sec[0] - - for comp in plates_sec[1:]: - plates = BRepAlgoAPI_Fuse(comp, plates).Shape() - - return plates - - def get_nut_bolt_models(self): - """ - :return: CAD model for all nut_bolt_arrangments - """ - nut_bolts_AF = self.nut_bolt_array_AF.get_modelsAF() - array_AF = nut_bolts_AF[0] - for comp in nut_bolts_AF: - array_AF = BRepAlgoAPI_Fuse(comp, array_AF).Shape() - - nut_bolts_BF = self.nut_bolt_array_BF.get_modelsBF() - array_BF = nut_bolts_BF[0] - for comp in nut_bolts_BF: - array_BF = BRepAlgoAPI_Fuse(comp, array_BF).Shape() - - nut_bolts_W = self.nut_bolt_array_Web.get_modelsW() - array_W = nut_bolts_W[0] - for comp in nut_bolts_W: - array_W = BRepAlgoAPI_Fuse(comp, array_W).Shape() - - nut_bolts_array = BRepAlgoAPI_Fuse(array_AF, array_BF).Shape() - nut_bolts_array = BRepAlgoAPI_Fuse(nut_bolts_array, array_W).Shape() - - return nut_bolts_array - - def get_only_column_models(self): - columns = self.get_column_models() - nutbolt = self.get_nut_bolt_models() - - onlycolumn = BRepAlgoAPI_Cut(columns, nutbolt).Shape() - - return onlycolumn - - def get_models(self): - columns = self.get_column_models() - plate_conectors = self.get_plate_models() - - CAD = BRepAlgoAPI_Fuse(columns, plate_conectors).Shape() - - return CAD - -if __name__ == '__main__': - from cad.items.ISection import ISection - from cad.items.plate import Plate - from cad.items.bolt import Bolt - from cad.items.nut import Nut - from cad.MomentConnections.CCSpliceCoverPlateCAD.nutBoltPlacement_AF import NutBoltArray_AF - from cad.MomentConnections.CCSpliceCoverPlateCAD.nutBoltPlacement_BF import NutBoltArray_BF - from cad.MomentConnections.CCSpliceCoverPlateCAD.nutBoltPlacement_Web import NutBoltArray_Web - import numpy - - import OCC.Core.V3d - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - display, start_display, add_menu, add_function_to_menu = init_display() - - column = ISection(B= 206.4, T= 17.3, D= 215.8, t= 10, R1= 15, R2= 75, alpha= 94, length= 1000, notchObj= None) - flangePlate = Plate(L= 240, W= 203.6, T= 10) - innerFlangePlate = Plate(L= 240, W= 85, T= 10) - webPlate = Plate(L= 600, W= 120, T= 8) - gap = 10 - - bolt = Bolt(R=12, T=5, H=6, r=6) - nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - nut_space = 2*flangePlate.T + column.T - Obj = '6' - numOfboltsF = 24 - plateAbvFlangeL = 100 - - nut_bolt_array_AF = NutBoltArray_AF(Obj, nut, bolt, numOfboltsF, nut_space) - nut_bolt_array_BF = NutBoltArray_BF(Obj, nut, bolt, numOfboltsF, nut_space) - numOfboltsF = 24 - nut_space = 2 * webPlate.T + column.t - nut_bolt_array_Web = NutBoltArray_Web(Obj, nut, bolt, numOfboltsF, nut_space) - - CCSpliceCoverPlateCAD = CCSpliceCoverPlateBoltedCAD(column, flangePlate, innerFlangePlate, webPlate, gap, nut_bolt_array_AF, nut_bolt_array_BF, nut_bolt_array_Web) - - CCSpliceCoverPlateCAD.create_3DModel() - column = CCSpliceCoverPlateCAD.get_column_models() - plates = CCSpliceCoverPlateCAD.get_plate_models() - nut_bolt_array = CCSpliceCoverPlateCAD.get_nut_bolt_models() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - - # display.View.Rotate(45, 90, 45) - display.DisplayShape(column, update=True) - display.DisplayColoredShape(plates, color='BLUE', update=True) - display.DisplayShape(nut_bolt_array, color='YELLOW', update=True) - - display.DisableAntiAliasing() - start_display() \ No newline at end of file diff --git a/cad/MomentConnections/CCSpliceCoverPlateCAD/WeldedCAD.py b/cad/MomentConnections/CCSpliceCoverPlateCAD/WeldedCAD.py deleted file mode 100644 index 0a8ab2201..000000000 --- a/cad/MomentConnections/CCSpliceCoverPlateCAD/WeldedCAD.py +++ /dev/null @@ -1,596 +0,0 @@ -""" -created on 14-04-2020 - -""" - -import numpy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse, BRepAlgoAPI_Cut -from cad.items.plate import Plate -import copy - - -class CCSpliceCoverPlateWeldedCAD(object): - def __init__(self, C, column, flangePlate, innerFlangePlate, webPlate, flangePlateWeldL, flangePlateWeldW, - innerflangePlateWeldL, innerflangePlateWeldW, webPlateWeldL, webPlateWeldW): - - self.C = C - self.column = column - self.flangePlate = flangePlate - self.innerFlangePlate = innerFlangePlate - self.webPlate = webPlate - self.flangePlateWeldL = flangePlateWeldL - self.flangePlateWeldW = flangePlateWeldW - self.webPlateWeldL = webPlateWeldL - self.webPlateWeldW = webPlateWeldW - self.innerflangePlateWeldL = innerflangePlateWeldL - self.innerflangePlateWeldW = innerflangePlateWeldW - - self.gap = float(self.C.flange_plate.gap) - self.flangespace = float(self.C.flangespace) - self.webspace = float(self.C.webspace) - - self.column1 = copy.deepcopy(self.column) - self.column2 = copy.deepcopy(self.column) - - self.flangePlate1 = copy.deepcopy(self.flangePlate) - self.flangePlate2 = copy.deepcopy(self.flangePlate) - - self.innerFlangePlate1 = copy.deepcopy(self.innerFlangePlate) - self.innerFlangePlate2 = copy.deepcopy(self.innerFlangePlate) - self.innerFlangePlate3 = copy.deepcopy(self.innerFlangePlate) - self.innerFlangePlate4 = copy.deepcopy(self.innerFlangePlate) - - self.webPlate1 = copy.deepcopy(self.webPlate) - self.webPlate2 = copy.deepcopy(self.webPlate) - - # nuber top to bottom - self.flangePlateWeldL11 = copy.deepcopy(self.flangePlateWeldL) - self.flangePlateWeldL12 = copy.deepcopy(self.flangePlateWeldL) - self.flangePlateWeldL21 = copy.deepcopy(self.flangePlateWeldL) - self.flangePlateWeldL22 = copy.deepcopy(self.flangePlateWeldL) - - self.flangePlateWeldW11 = copy.deepcopy(self.flangePlateWeldW) - self.flangePlateWeldW12 = copy.deepcopy(self.flangePlateWeldW) - self.flangePlateWeldW21 = copy.deepcopy(self.flangePlateWeldW) - self.flangePlateWeldW22 = copy.deepcopy(self.flangePlateWeldW) - - # Todo: update numbering - self.webPlateWeldL11 = copy.deepcopy(self.webPlateWeldL) - self.webPlateWeldL12 = copy.deepcopy(self.webPlateWeldL) - self.webPlateWeldL21 = copy.deepcopy(self.webPlateWeldL) - self.webPlateWeldL22 = copy.deepcopy(self.webPlateWeldL) - - self.webPlateWeldW11 = copy.deepcopy(self.webPlateWeldW) - self.webPlateWeldW12 = copy.deepcopy(self.webPlateWeldW) - self.webPlateWeldW21 = copy.deepcopy(self.webPlateWeldW) - self.webPlateWeldW22 = copy.deepcopy(self.webPlateWeldW) - - # numbering is clock wise starting from right side top plate - self.innerflangePlateWeldL11 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL12 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL21 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL22 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL31 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL32 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL41 = copy.deepcopy(self.innerflangePlateWeldL) - self.innerflangePlateWeldL42 = copy.deepcopy(self.innerflangePlateWeldL) - - self.innerflangePlateWeldW11 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW12 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW21 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW22 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW31 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW32 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW41 = copy.deepcopy(self.innerflangePlateWeldW) - self.innerflangePlateWeldW42 = copy.deepcopy(self.innerflangePlateWeldW) - - self.weldCutPlate = Plate(L=self.column.D + 4 * self.flangePlate.T, W=self.column.B + 2 * self.flangePlate.T, - T=self.gap) - - def create_3DModel(self): - ''' - :return: CAD model of each of the followings. Debugging each command below would give give clear picture - ''' - - self.createColumnGeometry() - self.createPlateGeometry() - self.createWeldedGeometry() - - - def createColumnGeometry(self): - """ - - :return: Geometric Orientation of this component - """ - column1Origin = numpy.array([0.0, 0.0, self.gap / 2]) - column1_uDir = numpy.array([1.0, 0.0, 0.0]) - column1_wDir = numpy.array([0.0, 0.0, 1.0]) - self.column1.place(column1Origin, column1_uDir, column1_wDir) - - self.column1Model = self.column1.create_model() - - column2Origin = numpy.array([0.0, 0.0, -self.gap / 2]) - column2_uDir = numpy.array([1.0, 0.0, 0.0]) - column2_wDir = numpy.array([0.0, 0.0, -1.0]) - self.column2.place(column2Origin, column2_uDir, column2_wDir) - - self.column2Model = self.column2.create_model() - - def createPlateGeometry(self): - flangePlate1Origin = numpy.array([-self.flangePlate.W / 2, (self.column.D + self.flangePlate.T) / 2, 0.0]) - flangePlate1_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlate1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.flangePlate1.place(flangePlate1Origin, flangePlate1_uDir, flangePlate1_wDir) - - self.flangePlate1Model = self.flangePlate1.create_model() - - flangePlate2Origin = numpy.array([-self.flangePlate.W / 2, -(self.column.D + self.flangePlate.T) / 2, 0.0]) - flangePlate2_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlate2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.flangePlate2.place(flangePlate2Origin, flangePlate2_uDir, flangePlate2_wDir) - - self.flangePlate2Model = self.flangePlate2.create_model() - - webPlate1Origin = numpy.array([(self.column.t + self.webPlate.T) / 2, -self.webPlate.W / 2, 0.0]) - webPlate1_uDir = numpy.array([1.0, 0.0, 0.0]) - webPlate1_wDir = numpy.array([0.0, 1.0, 0.0]) - self.webPlate1.place(webPlate1Origin, webPlate1_uDir, webPlate1_wDir) - - self.webPlate1Model = self.webPlate1.create_model() - - webPlate2Origin = numpy.array([-(self.column.t + self.webPlate.T) / 2, -self.webPlate.W / 2, 0.0]) - webPlate2_uDir = numpy.array([1.0, 0.0, 0.0]) - webPlate2_wDir = numpy.array([0.0, 1.0, 0.0]) - self.webPlate2.place(webPlate2Origin, webPlate2_uDir, webPlate2_wDir) - - self.webPlate2Model = self.webPlate2.create_model() - - if self.C.preference != 'Outside': - innerFlangePlatespacing = self.flangespace + self.column.t / 2 + self.column.R1 - innerFlangePlate1Origin = numpy.array( - [innerFlangePlatespacing, (self.column.D - self.innerFlangePlate.T) / 2 - self.column.T, 0.0]) - innerFlangePlate1_uDir = numpy.array([0.0, 1.0, 0.0]) - innerFlangePlate1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerFlangePlate1.place(innerFlangePlate1Origin, innerFlangePlate1_uDir, innerFlangePlate1_wDir) - - self.innerFlangePlate1Model = self.innerFlangePlate1.create_model() - - innerFlangePlate2Origin = numpy.array( - [innerFlangePlatespacing, -(self.column.D - self.innerFlangePlate.T) / 2 + self.column.T, 0.0]) - innerFlangePlate2_uDir = numpy.array([0.0, 1.0, 0.0]) - innerFlangePlate2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerFlangePlate2.place(innerFlangePlate2Origin, innerFlangePlate2_uDir, innerFlangePlate2_wDir) - - self.innerFlangePlate2Model = self.innerFlangePlate2.create_model() - - innerFlangePlate3Origin = numpy.array( - [-innerFlangePlatespacing, -(self.column.D - -- self.innerFlangePlate.T) / 2 + self.column.T, 0.0]) - innerFlangePlate3_uDir = numpy.array([0.0, 1.0, 0.0]) - innerFlangePlate3_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerFlangePlate3.place(innerFlangePlate3Origin, innerFlangePlate3_uDir, innerFlangePlate3_wDir) - - self.innerFlangePlate3Model = self.innerFlangePlate3.create_model() - - innerFlangePlate4Origin = numpy.array( - [-innerFlangePlatespacing, (self.column.D - self.innerFlangePlate.T) / 2 - self.column.T, 0.0]) - innerFlangePlate4_uDir = numpy.array([0.0, 1.0, 0.0]) - innerFlangePlate4_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerFlangePlate4.place(innerFlangePlate4Origin, innerFlangePlate4_uDir, innerFlangePlate4_wDir) - - self.innerFlangePlate4Model = self.innerFlangePlate4.create_model() - - def createWeldedGeometry(self): - - # Flangeplate1 - flangePlateWeldL11Origin = numpy.array( - [self.flangePlate.W / 2, (self.column.D) / 2, self.flangePlateWeldL.L / 2]) - flangePlateWeldL11_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlateWeldL11_wDir = numpy.array([0.0, 0.0, -1.0]) - self.flangePlateWeldL11.place(flangePlateWeldL11Origin, flangePlateWeldL11_uDir, flangePlateWeldL11_wDir) - - self.flangePlateWeldL11Model = self.flangePlateWeldL11.create_model() - - flangePlateWeldL12Origin = numpy.array( - [-self.flangePlate.W / 2, (self.column.D) / 2, -self.flangePlateWeldL.L / 2]) - flangePlateWeldL12_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlateWeldL12_wDir = numpy.array([0.0, 0.0, 1.0]) - self.flangePlateWeldL12.place(flangePlateWeldL12Origin, flangePlateWeldL12_uDir, flangePlateWeldL12_wDir) - - self.flangePlateWeldL12Model = self.flangePlateWeldL12.create_model() - - flangePlateWeldW11Origin = numpy.array( - [-self.flangePlate.W / 2, (self.column.D) / 2, self.flangePlateWeldL.L / 2]) - flangePlateWeldW11_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlateWeldW11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.flangePlateWeldW11.place(flangePlateWeldW11Origin, flangePlateWeldW11_uDir, flangePlateWeldW11_wDir) - - self.flangePlateWeldW11Model = self.flangePlateWeldW11.create_model() - - flangePlateWeldW12Origin = numpy.array( - [self.flangePlate.W / 2, (self.column.D) / 2, -self.flangePlateWeldL.L / 2]) - flangePlateWeldW12_uDir = numpy.array([0.0, 1.0, 0.0]) - flangePlateWeldW12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.flangePlateWeldW12.place(flangePlateWeldW12Origin, flangePlateWeldW12_uDir, flangePlateWeldW12_wDir) - - self.flangePlateWeldW12Model = self.flangePlateWeldW12.create_model() - - # FlangePlate2 - flangePlateWeldL21Origin = numpy.array( - [self.flangePlate.W / 2, -(self.column.D) / 2, -self.flangePlateWeldL.L / 2]) - flangePlateWeldL21_uDir = numpy.array([0.0, -1.0, 0.0]) - flangePlateWeldL21_wDir = numpy.array([0.0, 0.0, 1.0]) - self.flangePlateWeldL21.place(flangePlateWeldL21Origin, flangePlateWeldL21_uDir, flangePlateWeldL21_wDir) - - self.flangePlateWeldL21Model = self.flangePlateWeldL21.create_model() - - flangePlateWeldL22Origin = numpy.array( - [-self.flangePlate.W / 2, -(self.column.D) / 2, self.flangePlateWeldL.L / 2]) - flangePlateWeldL22_uDir = numpy.array([0.0, -1.0, 0.0]) - flangePlateWeldL22_wDir = numpy.array([0.0, 0.0, -1.0]) - self.flangePlateWeldL22.place(flangePlateWeldL22Origin, flangePlateWeldL22_uDir, flangePlateWeldL22_wDir) - - self.flangePlateWeldL22Model = self.flangePlateWeldL22.create_model() - - flangePlateWeldW21Origin = numpy.array( - [self.flangePlate.W / 2, -(self.column.D) / 2, self.flangePlateWeldL.L / 2]) - flangePlateWeldW21_uDir = numpy.array([0.0, -1.0, 0.0]) - flangePlateWeldW21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.flangePlateWeldW21.place(flangePlateWeldW21Origin, flangePlateWeldW21_uDir, flangePlateWeldW21_wDir) - - self.flangePlateWeldW21Model = self.flangePlateWeldW21.create_model() - - flangePlateWeldW22Origin = numpy.array( - [-self.flangePlate.W / 2, -(self.column.D) / 2, -self.flangePlateWeldL.L / 2]) - flangePlateWeldW22_uDir = numpy.array([0.0, -1.0, 0.0]) - flangePlateWeldW22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.flangePlateWeldW22.place(flangePlateWeldW22Origin, flangePlateWeldW22_uDir, flangePlateWeldW22_wDir) - - self.flangePlateWeldW22Model = self.flangePlateWeldW22.create_model() - - # Webplate1 (right side) - webPlateWeldL11Origin = numpy.array([(self.column.t) / 2, self.webPlate.W / 2, -self.webPlate.L / 2]) - webPlateWeldL11_uDir = numpy.array([1.0, 0.0, 0.0]) - webPlateWeldL11_wDir = numpy.array([0.0, 0.0, 1.0]) - self.webPlateWeldL11.place(webPlateWeldL11Origin, webPlateWeldL11_uDir, webPlateWeldL11_wDir) - - self.webPlateWeldL11Model = self.webPlateWeldL11.create_model() - - webPlateWeldL12Origin = numpy.array([(self.column.t) / 2, -self.webPlate.W / 2, -self.webPlate.L / 2]) - webPlateWeldL12_uDir = numpy.array([0.0, -1.0, 0.0]) - webPlateWeldL12_wDir = numpy.array([0.0, 0.0, 1.0]) - self.webPlateWeldL12.place(webPlateWeldL12Origin, webPlateWeldL12_uDir, webPlateWeldL12_wDir) - - self.webPlateWeldL12Model = self.webPlateWeldL12.create_model() - - webPlateWeldW11Origin = numpy.array([(self.column.t) / 2, self.webPlate.W / 2, self.webPlate.L / 2]) - webPlateWeldW11_uDir = numpy.array([1.0, 0.0, 0.0]) - webPlateWeldW11_wDir = numpy.array([0.0, -1.0, 0.0]) - self.webPlateWeldW11.place(webPlateWeldW11Origin, webPlateWeldW11_uDir, webPlateWeldW11_wDir) - - self.webPlateWeldW11Model = self.webPlateWeldW11.create_model() - - webPlateWeldW12Origin = numpy.array([(self.column.t) / 2, -self.webPlate.W / 2, -self.webPlate.L / 2]) - webPlateWeldW12_uDir = numpy.array([1.0, 0.0, 0.0]) - webPlateWeldW12_wDir = numpy.array([0.0, 1.0, 0.0]) - self.webPlateWeldW12.place(webPlateWeldW12Origin, webPlateWeldW12_uDir, webPlateWeldW12_wDir) - - self.webPlateWeldW12Model = self.webPlateWeldW12.create_model() - - # Webplate2 - webPlateWeldL21Origin = numpy.array([-(self.column.t) / 2, -self.webPlate.W / 2, -self.webPlate.L / 2]) - webPlateWeldL21_uDir = numpy.array([-1.0, 0.0, 0.0]) - webPlateWeldL21_wDir = numpy.array([0.0, 0.0, 1.0]) - self.webPlateWeldL21.place(webPlateWeldL21Origin, webPlateWeldL21_uDir, webPlateWeldL21_wDir) - - self.webPlateWeldL21Model = self.webPlateWeldL21.create_model() - - webPlateWeldL22Origin = numpy.array([-(self.column.t) / 2, self.webPlate.W / 2, self.webPlate.L / 2]) - webPlateWeldL22_uDir = numpy.array([-1.0, 0.0, 0.0]) - webPlateWeldL22_wDir = numpy.array([0.0, 0.0, -1.0]) - self.webPlateWeldL22.place(webPlateWeldL22Origin, webPlateWeldL22_uDir, webPlateWeldL22_wDir) - - self.webPlateWeldL22Model = self.webPlateWeldL22.create_model() - - webPlateWeldW21Origin = numpy.array([-(self.column.t) / 2, -self.webPlate.W / 2, self.webPlate.L / 2]) - webPlateWeldW21_uDir = numpy.array([-1.0, 0.0, 0.0]) - webPlateWeldW21_wDir = numpy.array([0.0, 1.0, 0.0]) - self.webPlateWeldW21.place(webPlateWeldW21Origin, webPlateWeldW21_uDir, webPlateWeldW21_wDir) - - self.webPlateWeldW21Model = self.webPlateWeldW21.create_model() - - webPlateWeldW22Origin = numpy.array([-(self.column.t) / 2, self.webPlate.W / 2, -self.webPlate.L / 2]) - webPlateWeldW22_uDir = numpy.array([-1.0, 0.0, 0.0]) - webPlateWeldW22_wDir = numpy.array([0.0, -1.0, 0.0]) - self.webPlateWeldW22.place(webPlateWeldW22Origin, webPlateWeldW22_uDir, webPlateWeldW22_wDir) - - self.webPlateWeldW22Model = self.webPlateWeldW22.create_model() - - if self.C.preference != 'Outside': - # innerplate1 (right top) - innerFlangePlatespacing = self.flangespace + self.column.t / 2 + self.column.R1 - innerflangePlateWeldL11Origin = numpy.array( - [innerFlangePlatespacing + self.innerFlangePlate.W, (self.column.D) / 2 - self.column.T, - -self.innerFlangePlate.L / 2]) - innerflangePlateWeldL11_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldL11_wDir = numpy.array([0.0, 0.0, 1.0]) - self.innerflangePlateWeldL11.place(innerflangePlateWeldL11Origin, innerflangePlateWeldL11_uDir, - innerflangePlateWeldL11_wDir) - - self.innerflangePlateWeldL11Model = self.innerflangePlateWeldL11.create_model() - - innerflangePlateWeldL12Origin = numpy.array( - [innerFlangePlatespacing, (self.column.D) / 2 - self.column.T, self.innerFlangePlate.L / 2]) - innerflangePlateWeldL12_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldL12_wDir = numpy.array([0.0, 0.0, -1.0]) - self.innerflangePlateWeldL12.place(innerflangePlateWeldL12Origin, innerflangePlateWeldL12_uDir, - innerflangePlateWeldL12_wDir) - - self.innerflangePlateWeldL12Model = self.innerflangePlateWeldL12.create_model() - - innerflangePlateWeldW11Origin = numpy.array( - [innerFlangePlatespacing + self.innerFlangePlate.W, (self.column.D) / 2 - self.column.T, - self.innerFlangePlate.L / 2]) - innerflangePlateWeldW11_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldW11_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerflangePlateWeldW11.place(innerflangePlateWeldW11Origin, innerflangePlateWeldW11_uDir, - innerflangePlateWeldW11_wDir) - - self.innerflangePlateWeldW11Model = self.innerflangePlateWeldW11.create_model() - - innerflangePlateWeldW12Origin = numpy.array( - [innerFlangePlatespacing, (self.column.D) / 2 - self.column.T, -self.innerFlangePlate.L / 2]) - innerflangePlateWeldW12_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldW12_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerflangePlateWeldW12.place(innerflangePlateWeldW12Origin, innerflangePlateWeldW12_uDir, - innerflangePlateWeldW12_wDir) - - self.innerflangePlateWeldW12Model = self.innerflangePlateWeldW12.create_model() - - # innerplate2 (top left) - innerflangePlateWeldL21Origin = numpy.array( - [-innerFlangePlatespacing, (self.column.D) / 2 - self.column.T, -self.innerFlangePlate.L / 2]) - innerflangePlateWeldL21_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldL21_wDir = numpy.array([0.0, 0.0, 1.0]) - self.innerflangePlateWeldL21.place(innerflangePlateWeldL21Origin, innerflangePlateWeldL21_uDir, - innerflangePlateWeldL21_wDir) - - self.innerflangePlateWeldL21Model = self.innerflangePlateWeldL21.create_model() - - innerflangePlateWeldL22Origin = numpy.array( - [-innerFlangePlatespacing - self.innerFlangePlate.W, (self.column.D) / 2 - self.column.T, - self.innerFlangePlate.L / 2]) - innerflangePlateWeldL22_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldL22_wDir = numpy.array([0.0, 0.0, -1.0]) - self.innerflangePlateWeldL22.place(innerflangePlateWeldL22Origin, innerflangePlateWeldL22_uDir, - innerflangePlateWeldL22_wDir) - - self.innerflangePlateWeldL22Model = self.innerflangePlateWeldL22.create_model() - - innerflangePlateWeldW21Origin = numpy.array( - [-innerFlangePlatespacing, (self.column.D) / 2 - self.column.T, self.innerFlangePlate.L / 2]) - innerflangePlateWeldW21_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldW21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerflangePlateWeldW21.place(innerflangePlateWeldW21Origin, innerflangePlateWeldW21_uDir, - innerflangePlateWeldW21_wDir) - - self.innerflangePlateWeldW21Model = self.innerflangePlateWeldW21.create_model() - - innerflangePlateWeldW22Origin = numpy.array( - [-innerFlangePlatespacing - self.innerFlangePlate.W, (self.column.D) / 2 - self.column.T, - -self.innerFlangePlate.L / 2]) - innerflangePlateWeldW22_uDir = numpy.array([0.0, -1.0, 0.0]) - innerflangePlateWeldW22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerflangePlateWeldW22.place(innerflangePlateWeldW22Origin, innerflangePlateWeldW22_uDir, - innerflangePlateWeldW22_wDir) - - self.innerflangePlateWeldW22Model = self.innerflangePlateWeldW22.create_model() - - # innerplate3 (Right bottom) - innerflangePlateWeldL31Origin = numpy.array( - [innerFlangePlatespacing + self.innerFlangePlate.W, -(self.column.D) / 2 + self.column.T, - self.innerFlangePlate.L / 2]) - innerflangePlateWeldL31_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldL31_wDir = numpy.array([0.0, 0.0, -1.0]) - self.innerflangePlateWeldL31.place(innerflangePlateWeldL31Origin, innerflangePlateWeldL31_uDir, - innerflangePlateWeldL31_wDir) - - self.innerflangePlateWeldL31Model = self.innerflangePlateWeldL31.create_model() - - innerflangePlateWeldL32Origin = numpy.array( - [innerFlangePlatespacing, -(self.column.D) / 2 + self.column.T, -self.innerFlangePlate.L / 2]) - innerflangePlateWeldL32_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldL32_wDir = numpy.array([0.0, 0.0, 1.0]) - self.innerflangePlateWeldL32.place(innerflangePlateWeldL32Origin, innerflangePlateWeldL32_uDir, - innerflangePlateWeldL32_wDir) - - self.innerflangePlateWeldL32Model = self.innerflangePlateWeldL32.create_model() - - innerflangePlateWeldW31Origin = numpy.array( - [innerFlangePlatespacing, -(self.column.D) / 2 + self.column.T, self.innerFlangePlate.L / 2]) - innerflangePlateWeldW31_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldW31_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerflangePlateWeldW31.place(innerflangePlateWeldW31Origin, innerflangePlateWeldW31_uDir, - innerflangePlateWeldW31_wDir) - - self.innerflangePlateWeldW31Model = self.innerflangePlateWeldW31.create_model() - - innerflangePlateWeldW32Origin = numpy.array( - [innerFlangePlatespacing + self.innerFlangePlate.W, -(self.column.D) / 2 + self.column.T, - -self.innerFlangePlate.L / 2]) - innerflangePlateWeldW32_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldW32_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerflangePlateWeldW32.place(innerflangePlateWeldW32Origin, innerflangePlateWeldW32_uDir, - innerflangePlateWeldW32_wDir) - - self.innerflangePlateWeldW32Model = self.innerflangePlateWeldW32.create_model() - - # innetplate4 (left bottom) - innerflangePlateWeldL41Origin = numpy.array( - [-innerFlangePlatespacing, -(self.column.D) / 2 + self.column.T, self.innerFlangePlate.L / 2]) - innerflangePlateWeldL41_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldL41_wDir = numpy.array([0.0, 0.0, -1.0]) - self.innerflangePlateWeldL41.place(innerflangePlateWeldL41Origin, innerflangePlateWeldL41_uDir, - innerflangePlateWeldL41_wDir) - - self.innerflangePlateWeldL41Model = self.innerflangePlateWeldL41.create_model() - - innerflangePlateWeldL42Origin = numpy.array( - [-innerFlangePlatespacing - self.innerFlangePlate.W, -(self.column.D) / 2 + self.column.T, - -self.innerFlangePlate.L / 2]) - innerflangePlateWeldL42_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldL42_wDir = numpy.array([0.0, 0.0, 1.0]) - self.innerflangePlateWeldL42.place(innerflangePlateWeldL42Origin, innerflangePlateWeldL42_uDir, - innerflangePlateWeldL42_wDir) - - self.innerflangePlateWeldL42Model = self.innerflangePlateWeldL42.create_model() - - innerflangePlateWeldW41Origin = numpy.array( - [-innerFlangePlatespacing - self.innerFlangePlate.W, -(self.column.D) / 2 + self.column.T, - self.innerFlangePlate.L / 2]) - innerflangePlateWeldW41_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldW41_wDir = numpy.array([1.0, 0.0, 0.0]) - self.innerflangePlateWeldW41.place(innerflangePlateWeldW41Origin, innerflangePlateWeldW41_uDir, - innerflangePlateWeldW41_wDir) - - self.innerflangePlateWeldW41Model = self.innerflangePlateWeldW41.create_model() - - innerflangePlateWeldW42Origin = numpy.array( - [-innerFlangePlatespacing, -(self.column.D) / 2 + self.column.T, -self.innerFlangePlate.L / 2]) - innerflangePlateWeldW42_uDir = numpy.array([0.0, 1.0, 0.0]) - innerflangePlateWeldW42_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.innerflangePlateWeldW42.place(innerflangePlateWeldW42Origin, innerflangePlateWeldW42_uDir, - innerflangePlateWeldW42_wDir) - - self.innerflangePlateWeldW42Model = self.innerflangePlateWeldW42.create_model() - - # to cut the welds - weldCutPlateOrigin = numpy.array([-self.weldCutPlate.W / 2, 0.0, 0.0]) - weldCutPlate_uDir = numpy.array([0.0, 0.0, 1.0]) - weldCutPlate_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldCutPlate.place(weldCutPlateOrigin, weldCutPlate_uDir, weldCutPlate_wDir) - - self.weldCutPlateModel = self.weldCutPlate.create_model() - - def get_column_models(self): - """ - - :return: CAD mode for the columns - """ - columns = BRepAlgoAPI_Fuse(self.column1Model, self.column2Model).Shape() - - return columns - - def get_plate_models(self): - """ - :return: CAD model for all the plates - """ - - - if self.C.preference != 'Outside': - plates_sec = [self.flangePlate1Model, self.flangePlate2Model, self.innerFlangePlate1Model, - self.innerFlangePlate2Model, self.innerFlangePlate3Model, self.innerFlangePlate4Model, - self.webPlate1Model, self.webPlate2Model] - else: - plates_sec = [self.flangePlate1Model, self.flangePlate2Model, - self.webPlate1Model, self.webPlate2Model] - - plates = plates_sec[0] - - for comp in plates_sec[1:]: - plates = BRepAlgoAPI_Fuse(comp, plates).Shape() - - return plates - - def get_welded_modules(self): - """ - :return: CAD model for all the welds - """ - if self.C.preference != 'Outside': - welded_sec = [self.flangePlateWeldL11Model, self.flangePlateWeldL12Model, self.flangePlateWeldL21Model, - self.flangePlateWeldL22Model, self.flangePlateWeldW11Model, self.flangePlateWeldW12Model, - self.flangePlateWeldW21Model, self.flangePlateWeldW22Model, \ - self.webPlateWeldL11Model, self.webPlateWeldL12Model, self.webPlateWeldL21Model, - self.webPlateWeldL22Model, self.webPlateWeldW11Model, self.webPlateWeldW12Model, - self.webPlateWeldW21Model, self.webPlateWeldW22Model, \ - self.innerflangePlateWeldL11Model, self.innerflangePlateWeldL12Model, - self.innerflangePlateWeldL21Model, self.innerflangePlateWeldL22Model, - self.innerflangePlateWeldL31Model, self.innerflangePlateWeldL32Model, - self.innerflangePlateWeldL41Model, self.innerflangePlateWeldL42Model, \ - self.innerflangePlateWeldW11Model, self.innerflangePlateWeldW12Model, - self.innerflangePlateWeldW21Model, self.innerflangePlateWeldW22Model, - self.innerflangePlateWeldW31Model, self.innerflangePlateWeldW32Model, - self.innerflangePlateWeldW41Model, self.innerflangePlateWeldW42Model] - else: - welded_sec = [self.flangePlateWeldL11Model, self.flangePlateWeldL12Model, self.flangePlateWeldL21Model, - self.flangePlateWeldL22Model, self.flangePlateWeldW11Model, self.flangePlateWeldW12Model, - self.flangePlateWeldW21Model, self.flangePlateWeldW22Model, \ - self.webPlateWeldL11Model, self.webPlateWeldL12Model, self.webPlateWeldL21Model, - self.webPlateWeldL22Model, self.webPlateWeldW11Model, self.webPlateWeldW12Model, - self.webPlateWeldW21Model, self.webPlateWeldW22Model] - - welds = welded_sec[0] - - for comp in welded_sec[1:]: - welds = BRepAlgoAPI_Fuse(comp, welds).Shape() - - welds = BRepAlgoAPI_Cut(welds, self.weldCutPlateModel).Shape() - - return welds - # return self.innerflangePlateWeldW42Model - - def get_models(self): - columns = self.get_column_models() - plate_conectors = self.get_plate_models() - welds = self.get_welded_modules() - - CAD = BRepAlgoAPI_Fuse(columns, plate_conectors).Shape() - CAD = BRepAlgoAPI_Fuse(CAD, welds).Shape() - - return CAD - -if __name__ == '__main__': - from cad.items.ISection import ISection - from cad.items.plate import Plate - from cad.items.filletweld import FilletWeld - - import OCC.Core.V3d - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - column = ISection(B=250, T=13.5, D=450, t=9.8, R1=15, R2=75, alpha=94, length=1000, notchObj=None) - flangePlate = Plate(L=550, W=210, T=14) - innerFlangePlate = Plate(L=550, W=80, T=14) - webPlate = Plate(L=365, W=170, T=8) - gap = 10 - - flangePlateWeldL = FilletWeld(h=5, b=5, L=flangePlate.L) - flangePlateWeldW = FilletWeld(h=5, b=5, L=flangePlate.W) - - innerflangePlateWeldL = FilletWeld(h=5, b=5, L=innerFlangePlate.L) - innerflangePlateWeldW = FilletWeld(h=5, b=5, L=innerFlangePlate.W) - - webPlateWeldL = FilletWeld(h=5, b=5, L=webPlate.L) - webPlateWeldW = FilletWeld(h=5, b=5, L=webPlate.W) - - CCSpliceCoverPlateCAD = CCSpliceCoverPlateWeldedCAD(column, flangePlate, innerFlangePlate, webPlate, gap, - flangePlateWeldL, flangePlateWeldW, innerflangePlateWeldL, - innerflangePlateWeldW, webPlateWeldL, webPlateWeldW) - - CCSpliceCoverPlateCAD.create_3DModel() - column = CCSpliceCoverPlateCAD.get_column_models() - plates = CCSpliceCoverPlateCAD.get_plate_models() - welds = CCSpliceCoverPlateCAD.get_welded_modules() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - # display.View.Rotate(45, 90, 45) - display.DisplayShape(column, update=True) - display.DisplayShape(plates, color='BLUE', update=True) - display.DisplayShape(welds, color='RED', update=True) - - display.DisableAntiAliasing() - start_display() diff --git a/cad/MomentConnections/CCSpliceCoverPlateCAD/nutBoltPlacement_AF.py b/cad/MomentConnections/CCSpliceCoverPlateCAD/nutBoltPlacement_AF.py deleted file mode 100644 index b6eb473c1..000000000 --- a/cad/MomentConnections/CCSpliceCoverPlateCAD/nutBoltPlacement_AF.py +++ /dev/null @@ -1,220 +0,0 @@ -""" -created on 25-02-2018 - -@author: Siddhesh Chavan - -AF abbreviation used here is for Above Flange for bolting. -BF abbreviation used here is for Below Flange for bolting. -W is for bolting over Web. - -""""" - -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse - - -class NutBoltArray_AF(): - def __init__(self, Obj, nut, bolt, numOfboltsF, nutSpaceF): - """ - :param alist: Input values, entered by user - :param beam_data: Beam dimensions - :param outputobj: Output dictionary - :param nut: Nut dimensions - :param bolt: Bolt dimensions - :param numOfboltsF: Number of bolts required for over plate above flange - :param nutSpaceF: Spacing between bolt head and nut - """ - self.boltOrigin_AF = None - self.pitch_new_AF = None - self.originAF = None - self.gaugeDirAF = None - self.pitchDirAF = None - self.boltDirAF = None - - # self.uiObj = alist - # self.beamDim = beam_data - self.bolt = bolt - self.nut = nut - self.numOfboltsF = numOfboltsF - self.nutSpaceF = nutSpaceF - - self.initBoltPlaceParams_AF(Obj) - self.bolts_AF = [] - self.nuts_AF = [] - self.initialiseNutBolts_AF() - self.positions_AF = [] - self.models_AF = [] - - ################################################################# - # Nut_Bolt placement above flange(AF) of beam # - ################################################################# - - def initialiseNutBolts_AF(self): - ''' - :return: This initializes required number of bolts and nuts for above flange. - ''' - b_AF = self.bolt - n_AF = self.nut - - for i in range(self.numOfboltsF): - bolt_length_required = float(n_AF.H + self.nutSpaceF) # todo: anjali - print(bolt_length_required, "len") - # bolt_length_required = float(b_AF.T + self.nutSpaceF) - # bolt_length_required = 100 - b_AF.H = bolt_length_required + 10 - self.bolts_AF.append(Bolt(b_AF.R, b_AF.T, b_AF.H, b_AF.r)) - print("bolt", b_AF.R, b_AF.T, b_AF.H, b_AF.r) - self.nuts_AF.append(Nut(n_AF.R, n_AF.T, n_AF.H, n_AF.r1)) - print('Nut', (n_AF.R, n_AF.T, n_AF.H, n_AF.r1)) - - def initBoltPlaceParams_AF(self, Obj): - ''' - :param outputobj: This is output dictionary for bolt placement parameters - :return: Edge, end, gauge and pitch distances for placement - ''' - - self.edge_AF = Obj.flange_plate.edge_dist_provided - self.end_AF = Obj.flange_plate.end_dist_provided - self.edge_gauge_AF = Obj.flange_plate.edge_dist_provided - self.pitch_AF = Obj.flange_plate.pitch_provided - self.midpitch_AF = Obj.flange_plate.midpitch #=(2 * self.flange_plate.end_dist_provided) + self.flange_plate.gap + self.section.web_thickness - self.gauge_AF = Obj.flange_plate.midgauge - self.gauge = Obj.flange_plate.gauge_provided - - # outputobj.flange_plate.gauge_provided # Revised gauge distance #0.0 - self.row_AF = Obj.flange_plate.bolt_line - self.col_AF = Obj.flange_plate.bolts_one_line #2 - self.gap = Obj.flange_plate.gap - # if self.col_AF ==2: - # self.gauge =0 - # self.gauge_AF= Obj.flange_plate.midgauge - # else: - # self.gauge = Obj.flange_plate.gauge_provided - # self.gauge_AF= Obj.flange_plate.midgauge - - def calculatePositions_AF(self): - """ - :return: The positions/coordinates to place the bolts in the form of list, positions_AF = [list of bolting coordinates] - """ - self.positions_AF = [] - self.boltOrigin_AF = self.originAF + self.end_AF * self.pitchDirAF + (self.edge_AF) * self.gaugeDirAF - # self.boltOrigin_AF = self.originAF - (self.row_AF/2 * self.pitch_AF) * self.pitchDirAF - ((self.col_AF / 2) * self.gauge) * self.gaugeDirAF - # + ((self.plateAbvFlangeL - self.gauge_AF) / 2 - ((self.col_AF / 2 - 1) * self.gauge)) * self.gaugeDirAF - - for rw_AF in range(self.row_AF): - for cl_AF in range(self.col_AF): - pos_AF = self.boltOrigin_AF - if self.row_AF / 2 < rw_AF or self.row_AF / 2 == rw_AF: - self.pitch_new_AF = self.midpitch_AF - pos_AF = pos_AF + ((rw_AF - 1) * self.pitch_AF + self.pitch_new_AF) * self.pitchDirAF - if self.col_AF / 2 > cl_AF: - pos_AF = pos_AF + cl_AF * self.gauge * self.gaugeDirAF - else: - pos_AF = pos_AF + ( - cl_AF - 1) * self.gauge * self.gaugeDirAF + 1 * self.gauge_AF * self.gaugeDirAF - self.positions_AF.append(pos_AF) - else: - pos_AF = pos_AF + rw_AF * self.pitch_AF * self.pitchDirAF - if self.col_AF / 2 > cl_AF: - pos_AF = pos_AF + cl_AF * self.gauge * self.gaugeDirAF - else: - pos_AF = pos_AF + ( - cl_AF - 1) * self.gauge * self.gaugeDirAF + 1 * self.gauge_AF * self.gaugeDirAF - self.positions_AF.append(pos_AF) - - def placeAF(self, originAF, gaugeDirAF, pitchDirAF, boltDirAF, plateAbvFlangeL): - self.originAF = originAF - self.gaugeDirAF = gaugeDirAF - self.pitchDirAF = pitchDirAF - self.boltDirAF = boltDirAF - self.plateAbvFlangeL = plateAbvFlangeL - - self.calculatePositions_AF() - - for index, pos in enumerate(self.positions_AF): - self.bolts_AF[index].place(pos, gaugeDirAF, boltDirAF) - self.nuts_AF[index].place((pos + self.nutSpaceF * boltDirAF), gaugeDirAF, boltDirAF) - - def create_modelAF(self): - print("hhhh", self.bolts_AF) - for bolt in self.bolts_AF: - print("bolt", bolt, "fgfg") - self.models_AF.append(bolt.create_model()) - - for nut in self.nuts_AF: - self.models_AF.append(nut.create_model()) - pass - - dbg = self.dbgSphere(self.originAF) - self.models_AF.append(dbg) - - def dbgSphere(self, pt): - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_modelsAF(self): - # nut_bolts = self.models_AF - # array = nut_bolts[0] - # for comp in nut_bolts: - # array = BRepAlgoAPI_Fuse(comp, array).Shape() - # - # return array - #todo: using for loops is here is slowing down the cad generating process - return self.models_AF - - # Below methods are for creating holes in flange and web - def get_bolt_listLA(self): - boltlist = [] - for bolt in self.bolts_AF: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originAF) - self.models_AF.append(dbg) - return boltlist - - def get_bolt_listRA(self): - boltlist = [] - for bolt in self.bolts_AF: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originAF) - self.models_AF.append(dbg) - return boltlist - - -if __name__ == '__main__': - from cad.items.bolt import Bolt - from cad.items.nut import Nut - import numpy - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - nutboltArrayOrigin = numpy.array([0., 0., 0.]) - gaugeDir = numpy.array([0.0, 1.0, 0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 0, 1.0]) - - bolt = Bolt(R=12, T=5, H=6, r=6) - nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - nut_space = 10 + 5 + nut.T # member.T + plate.T + nut.T - Obj = '6' - numOfboltsF = 24 - plateAbvFlangeL = 100 - - nut_bolt_array = NutBoltArray_AF(Obj, nut, bolt, numOfboltsF, nut_space) - - nut_bolt_array.placeAF(nutboltArrayOrigin, pitchDir, gaugeDir, boltDir, plateAbvFlangeL) - nut_bolt_array.create_modelAF() - - array = nut_bolt_array.get_modelsAF() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(array, color='YELLOW', update=True) - # display.DisplayShape(parray, color= 'BLUE', update=True) - display.DisableAntiAliasing() - start_display() diff --git a/cad/MomentConnections/CCSpliceCoverPlateCAD/nutBoltPlacement_BF.py b/cad/MomentConnections/CCSpliceCoverPlateCAD/nutBoltPlacement_BF.py deleted file mode 100644 index 4a12a8bc6..000000000 --- a/cad/MomentConnections/CCSpliceCoverPlateCAD/nutBoltPlacement_BF.py +++ /dev/null @@ -1,204 +0,0 @@ -""" -created on 25-02-2018 - -@author: Siddhesh Chavan - -AF abbreviation used here is for Above Flange for bolting. -BF abbreviation used here is for Below Flange for bolting. -W is for bolting over Web. -""""" - -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt - - -class NutBoltArray_BF(): - def __init__(self, Obj, nut, bolt, numOfboltsF, nutSpaceF): - """ - :param alist: Input values, entered by user - :param beam_data: Beam dimensions - :param outputobj: Output dictionary - :param nut: Nut dimensions - :param bolt: Bolt dimensions - :param numOfboltsF: Number of bolts required for over plate above flange - :param nutSpaceF: Spacing between bolt head and nut - """ - self.boltOrigin_BF = None - self.pitch_new_BF = None - self.originBF = None - self.gaugeDirBF = None - self.pitchDirBF = None - self.boltDirBF = None - - # self.uiObj = alist - # self.beamDim = beam_data - self.bolt = bolt - self.nut = nut - self.numOfboltsF = numOfboltsF - self.nutSpaceF = nutSpaceF - - self.initBoltPlaceParams_BF(Obj) - self.bolts_BF = [] - self.nuts_BF = [] - self.initialiseNutBolts_BF() - self.positions_BF = [] - self.models_BF = [] - - ################################################################# - # Nut_Bolt placement below flange(BF) of beam # - ################################################################# - - def initialiseNutBolts_BF(self): - ''' - :return: This initializes required number of bolts and nuts for below flange. - ''' - b_BF = self.bolt - n_BF = self.nut - for j in range(self.numOfboltsF): - bolt_length_required = float(n_BF.H + self.nutSpaceF) - b_BF.H = bolt_length_required + 10 - self.bolts_BF.append(Bolt(b_BF.R, b_BF.T, b_BF.H, b_BF.r)) - self.nuts_BF.append(Nut(n_BF.R, n_BF.T, n_BF.H, n_BF.r1)) - - def initBoltPlaceParams_BF(self, Obj): - ''' - :param outputobj: This is output dictionary for bolt placement parameters - :return: Edge, end, gauge and pitch distances for placement - ''' - self.edge_BF = Obj.flange_plate.edge_dist_provided - self.end_BF = Obj.flange_plate.end_dist_provided - self.edge_gauge_BF = Obj.flange_plate.edge_dist_provided - self.pitch_BF = Obj.flange_plate.pitch_provided - self.gauge_BF = Obj.flange_plate.midgauge - self.gauge = Obj.flange_plate.gauge_provided - # outputobj.flange_plate.gauge_provided # Revised gauge distance - self.row_BF = Obj.flange_plate.bolt_line - self.col_BF = Obj.flange_plate.bolts_one_line - self.gap = Obj.flange_plate.gap - - def calculatePositions_BF(self): - """ - :return: The positions/coordinates to place the bolts in the form of list, positions_BF = [list of bolting coordinates] - """ - self.positions_BF = [] - # self.boltOrigin_BF = self.originBF - (self.row_BF/2* self.pitch_BF) * self.pitchDirBF - (((self.col_BF / 2) * self.gauge)) * self.gaugeDirBF - self.boltOrigin_BF = self.originBF + self.end_BF * self.pitchDirBF + (self.edge_BF) * self.gaugeDirBF - - for rw_BF in range(self.row_BF): - for cl_BF in range(self.col_BF): - pos_BF = self.boltOrigin_BF - if self.row_BF / 2 < rw_BF or self.row_BF / 2 == rw_BF: - self.pitch_new_BF = 2 * self.end_BF + self.gap - pos_BF = pos_BF + ((rw_BF - 1) * self.pitch_BF + self.pitch_new_BF) * self.pitchDirBF - if self.col_BF / 2 > cl_BF: - pos_BF = pos_BF + cl_BF * self.gauge * self.gaugeDirBF - else: - pos_BF = pos_BF + ( - cl_BF - 1) * self.gauge * self.gaugeDirBF + 1 * self.gauge_BF * self.gaugeDirBF - self.positions_BF.append(pos_BF) - else: - pos_BF = pos_BF + rw_BF * self.pitch_BF * self.pitchDirBF - if self.col_BF / 2 > cl_BF: - pos_BF = pos_BF + cl_BF * self.gauge * self.gaugeDirBF - else: - pos_BF = pos_BF + ( - cl_BF - 1) * self.gauge * self.gaugeDirBF + 1 * self.gauge_BF * self.gaugeDirBF - self.positions_BF.append(pos_BF) - - # pos_BF = self.boltOrigin_BF - # # if self.row_BF / 2 < rw_BF or self.row_BF / 2 == rw_BF: - # # self.pitch_new_BF = 2 * self.edge_gauge_BF + self.gap - # # pos_BF = pos_BF + ((rw_BF - 1) * self.pitch_BF + self.pitch_new_BF) * self.pitchDirBF - # # pos_BF = pos_BF + cl_BF * self.gauge_BF * self.gaugeDirBF - # # self.positions_BF.append(pos_BF) - # # else: - # # pos_BF = pos_BF + rw_BF * self.pitch_BF * self.pitchDirBF - # # pos_BF = pos_BF + cl_BF * self.gauge_BF * self.gaugeDirBF - # # self.positions_BF.append(pos_BF) - - def placeBF(self, originBF, gaugeDirBF, pitchDirBF, boltDirBF, plateBelwFlangeL): - self.originBF = originBF - self.gaugeDirBF = gaugeDirBF - self.pitchDirBF = pitchDirBF - self.boltDirBF = boltDirBF - self.plateBelwFlangeL = plateBelwFlangeL - - self.calculatePositions_BF() - - for index_BF, pos_BF in enumerate(self.positions_BF): - self.bolts_BF[index_BF].place(pos_BF, gaugeDirBF, boltDirBF) - self.nuts_BF[index_BF].place((pos_BF + self.nutSpaceF * boltDirBF), gaugeDirBF, boltDirBF) - - def create_modelBF(self): - for bolt in self.bolts_BF: - self.models_BF.append(bolt.create_model()) - pass - for nut in self.nuts_BF: - self.models_BF.append(nut.create_model()) - pass - - dbg = self.dbgSphere(self.originBF) - self.models_BF.append(dbg) - - def dbgSphere(self, pt): - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_modelsBF(self): - return self.models_BF - - # Below methods are for creating holes in flange and web - def get_bolt_listLB(self): - boltlist = [] - for bolt in self.bolts_BF: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originBF) - self.models_BF.append(dbg) - return boltlist - - def get_bolt_listRB(self): - boltlist = [] - for bolt in self.bolts_BF: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originBF) - self.models_BF.append(dbg) - return boltlist - - -if __name__ == '__main__': - from cad.items.bolt import Bolt - from cad.items.nut import Nut - import numpy - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - nutboltArrayOrigin = numpy.array([0., 0., 0.]) - gaugeDir = numpy.array([0.0, 1.0, 0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - - bolt = Bolt(R=12, T=5, H=6, r=6) - nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - nut_space = 10 + 5 + nut.T # member.T + plate.T + nut.T - Obj = '6' - boltDir = numpy.array([0, 0, 1.0]) - numOfboltsF = 24 - plateAbvFlangeL = 100 - - nut_bolt_array = NutBoltArray_BF(Obj, nut, bolt, numOfboltsF, nut_space) - - nut_bolt_array.placeBF(nutboltArrayOrigin, pitchDir, gaugeDir, boltDir, plateAbvFlangeL) - nut_bolt_array.create_modelBF() - - array = nut_bolt_array.get_modelsBF() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(array, color='YELLOW', update=True) - # display.DisplayShape(parray, color= 'BLUE', update=True) - display.DisableAntiAliasing() - start_display() diff --git a/cad/MomentConnections/CCSpliceCoverPlateCAD/nutBoltPlacement_Web.py b/cad/MomentConnections/CCSpliceCoverPlateCAD/nutBoltPlacement_Web.py deleted file mode 100644 index 0f5118f14..000000000 --- a/cad/MomentConnections/CCSpliceCoverPlateCAD/nutBoltPlacement_Web.py +++ /dev/null @@ -1,202 +0,0 @@ -""" -created on 25-02-2018 - -@author: Siddhesh Chavan - -AF abbreviation used here is for Above Flange for bolting. -BF abbreviation used here is for Below Flange for bolting. -W is for bolting over Web. -""""" - -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt - - -class NutBoltArray_Web(): - def __init__(self, Obj, nut, bolt, numOfboltsW, nutSpaceW): - """ - :param alist: Input values, entered by user - :param beam_data: Beam dimensions - :param outputobj: Output dictionary - :param nut: Nut dimensions - :param bolt: Bolt dimensions - :param numOfboltsF: Number of bolts required for over plate above flange - :param nutSpaceF: Spacing between bolt head and nut - """ - self.boltOrigin_W = None - self.originW = None - self.gaugeDirW = None - self.pitchDirW = None - self.boltDirW = None - - # self.uiObj = alist - # self.beamDim = beam_data - self.bolt = bolt - self.nut = nut - self.numOfboltsW = numOfboltsW - self.nutSpaceW = nutSpaceW - - self.initBoltPlaceParams_Web(Obj) - self.bolts_W = [] - self.nuts_W = [] - self.initialiseNutBolts_Web() - self.positions_W = [] - self.models_W = [] - - ################################################################# - # Nut_Bolt placement over Web of beam # - ################################################################# - - def initialiseNutBolts_Web(self): - ''' - :return: This initializes required number of bolts and nuts for web bolting. - ''' - b_W = self.bolt - n_W = self.nut - for k in range(self.numOfboltsW): - bolt_length_required = float(n_W.H + self.nutSpaceW) - b_W.H = bolt_length_required + 10 - self.bolts_W.append(Bolt(b_W.R, b_W.T, b_W.H, b_W.r)) - self.nuts_W.append(Nut(n_W.R, n_W.T, n_W.H, n_W.r1)) - - def initBoltPlaceParams_Web(self, Obj): - ''' - :param outputobj: This is output dictionary for bolt placement parameters - :return: Edge, end, gauge and pitch distances for placement - ''' - self.edge_W = Obj.web_plate.edge_dist_provided # 33 - self.end_W = Obj.web_plate.end_dist_provided # 33 - # self.pitch_W = 150 #70 - # self.gauge_W = outputobj.web_plate.length - 2* self.edge_W - self.pitch_W = Obj.web_plate.pitch_provided - self.pitch_MW = Obj.web_plate.midpitch # todo for gap - self.gauge_W = Obj.web_plate.gauge_provided - - self.row_W = Obj.web_plate.bolts_one_line - self.col_W = Obj.web_plate.bolt_line - - def calculatePositions_Web(self): - """ - - :return: The positions/coordinates to place the bolts in the form of list, positions_W = [list of bolting coordinates] - """ - self.positions_W = [] - # self.boltOrigin_W = self.originW - ((self.row_W/2 * self.gauge_W) - self.end_W/2) * self.pitchDirW - (((self.col_W/2)*self.pitch_W)*self.gaugeDirW) - self.boltOrigin_W = self.originW + self.end_W * self.pitchDirW + (self.edge_W) * self.gaugeDirW - for rw_W in range(self.row_W): - for cl_W in range(self.col_W): - pos_W = self.boltOrigin_W - pos_W = pos_W + rw_W * self.gauge_W * self.pitchDirW - print(self.gauge_W) - if self.col_W / 2 > cl_W: - pos_W = pos_W + cl_W * self.pitch_W * self.gaugeDirW - else: - pos_W = pos_W + (cl_W - 1) * self.pitch_W * self.gaugeDirW + 1 * self.pitch_MW * self.gaugeDirW - self.positions_W.append(pos_W) - # else: - # pos_AF = pos_AF + rw_W * self.pitch_AF * self.pitchDirAF - # if self.col_AF / 2 > cl_AF: - # pos_AF = pos_AF + cl_AF * self.gauge * self.gaugeDirAF - # else: - # pos_AF = pos_AF + ( - # cl_AF - 1) * self.gauge * self.gaugeDirAF + 1 * self.gauge_AF * self.gaugeDirAF - # self.positions_AF.append(pos_AF) - - # pos_W = pos_W + rw_W * self.pitch_W * self.pitchDirW - # pos_W = pos_W + cl_W * self.gauge_W * self.gaugeDirW - # pos_W = pos_W + rw_W * self.gauge_W * self.pitchDirW - # pos_W = pos_W + cl_W * self.pitch_W * self.gaugeDirW - # - # - # self.positions_W.append(pos_W) - - def placeW(self, originW, gaugeDirW, pitchDirW, boltDirW): - """ - :param originW: Origin for bolt placement - :param gaugeDirW: Gauge direction for gauge distance - :param pitchDirW: Pitch direction for pitch distance - :param boltDirW: Bolt screwing direction - :return: - """ - self.originW = originW - self.gaugeDirW = gaugeDirW - self.pitchDirW = pitchDirW - self.boltDirW = boltDirW - - self.calculatePositions_Web() - for index_W, pos_W in enumerate(self.positions_W): - self.bolts_W[index_W].place(pos_W, gaugeDirW, boltDirW) - self.nuts_W[index_W].place((pos_W + self.nutSpaceW * boltDirW), gaugeDirW, boltDirW) - - def create_modelW(self): - for bolt in self.bolts_W: - self.models_W.append(bolt.create_model()) - pass - for nut in self.nuts_W: - self.models_W.append(nut.create_model()) - pass - - dbg = self.dbgSphere(self.originW) - self.models_W.append(dbg) - - def dbgSphere(self, pt): - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_modelsW(self): - return self.models_W - - def get_bolt_web_list(self): - boltlist = [] - for bolt in self.bolts_W: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originW) - self.models_W.append(dbg) - return boltlist - - def get_bolt_listRB(self): - boltlist = [] - for bolt in self.bolts_W: - boltlist.append(bolt.create_model()) - dbg = self.dbgSphere(self.originW) - self.models_W.append(dbg) - return boltlist - - -if __name__ == '__main__': - from cad.items.bolt import Bolt - from cad.items.nut import Nut - import numpy - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - nutboltArrayOrigin = numpy.array([0., 0., 0.]) - gaugeDir = numpy.array([0.0, 1.0, 0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 0, 1.0]) - - bolt = Bolt(R=12, T=5, H=6, r=6) - nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - nut_space = 10 + 5 + nut.T # member.T + plate.T + nut.T - Obj = '6' - numOfboltsF = 24 - plateAbvFlangeL = 100 - - nut_bolt_array = NutBoltArray_Web(Obj, nut, bolt, numOfboltsF, nut_space) - - nut_bolt_array.placeW(nutboltArrayOrigin, pitchDir, gaugeDir, boltDir) - nut_bolt_array.create_modelW() - - array = nut_bolt_array.get_modelsW() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(array, color='YELLOW', update=True) - # display.DisplayShape(parray, color= 'BLUE', update=True) - display.DisableAntiAliasing() - start_display() diff --git a/cad/ShearConnections/CleatAngle/beamWebBeamWebConnectivity.py b/cad/ShearConnections/CleatAngle/beamWebBeamWebConnectivity.py deleted file mode 100644 index 23844a7fc..000000000 --- a/cad/ShearConnections/CleatAngle/beamWebBeamWebConnectivity.py +++ /dev/null @@ -1,165 +0,0 @@ -''' -Created on 10-Mar-2016 - -@author: deepa -''' -''' -Created on 11-May-2015 - -@author: deepa -''' - -import numpy -import copy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut - - -class BeamWebBeamWeb(object): - - def __init__(self, column, beam, notch, angle, nut_bolt_array,gap): - self.column = column - self.beam = beam - self.notch = notch - self.angle = angle - self.angleLeft = copy.deepcopy(angle) - self.nut_bolt_array = nut_bolt_array - self.gap = gap - self.fillet_gap = self.gap - 0.1 # 0.1 is the extra margin for fillet of the angel - self.columnModel = None - self.beamModel = None - self.angleModel = None - self.angleLeftModel = None - self.clearDist = 20.0 # This distance between edge of the column web/flange and beam cross section - - def create_3dmodel(self): - self.create_column_geometry() - self.create_beam_geometry() - self.create_angle_geometry() - self.create_nut_bolt_array() - - # Call for create_model - self.columnModel = self.column.create_model() - self.beamModel = self.beam.create_model() - self.angleModel = self.angle.create_model() - self.angleLeftModel = self.angleLeft.create_model() - self.nutboltArrayModels = self.nut_bolt_array.create_model() -# self.notchModel = self.notch.create_model() -# return self.beamModel - -# def create_column_geometry(self): -# column_origin = numpy.array([0, 0, 0]) -# column_u_dir = numpy.array([1.0, 0, 0]) -# wDir1 = numpy.array([0.0, 1.0, 0.0]) -# self.column.place(column_origin, column_u_dir, wDir1) -# -# def create_beam_geometry(self): -# uDir = numpy.array([0, 1.0, 0]) -# wDir = numpy.array([1.0, 0, 0.0]) -# shiftOrigin = (self.column.D/2 - self.beam.D/2) -# origin2 = self.column.sec_origin + (-shiftOrigin) * self.column.vDir + (self.column.t/2 * self.column.uDir) + (self.column.length/2 * self.column.wDir) + (self.gap * self.column.uDir) -# self.beam.place(origin2, uDir, wDir) - - def create_column_geometry(self): - - column_origin = numpy.array([0, 0, 0]) - column_u_dir = numpy.array([1.0, 0, 0]) - wDir1 = numpy.array([0.0, -1.0, 0.0]) - self.column.place(column_origin, column_u_dir, wDir1) - - def create_beam_geometry(self): - beam_origin = ((self.column.sec_origin + self.column.D / 2 - self.beam.D / 2) * (self.column.vDir)) + \ - ((self.column.t / 2 + self.gap) * self.column.uDir) + \ - (self.column.length / 2 * (self.column.wDir)) - uDir = numpy.array([0.0, 1.0, 0]) - wDir = numpy.array([1.0, 0.0, 0.0]) - self.beam.place(beam_origin, uDir, wDir) - - - def create_angle_geometry(self): - angle0_origin = (self.beam.sec_origin + (self.beam.D / 2.0 - self.notch.height - self.angle.L) - * (self.beam.vDir) - (self.beam.t / 2 * self.beam.uDir) + self.fillet_gap - * (-self.beam.wDir)) - uDir0 = numpy.array([0, -1.0, 0]) - wDir0 = numpy.array([0.0, 0, 1.0]) - - self.angleLeft.place(angle0_origin, uDir0, wDir0) - - angle1_origin = (self.beam.sec_origin + (self.beam.D / 2.0 - self.notch.height) - * (self.beam.vDir) + ((self.beam.t / 2 ) * self.beam.uDir)+ self.fillet_gap - * (-self.beam.wDir)) - uDir1 = numpy.array([0, 1.0, 0]) - wDir1 = numpy.array([0, 0, -1.0]) - self.angle.place(angle1_origin, uDir1, wDir1) - - def create_nut_bolt_array(self): - """ - - :return: - """ - nut_bolt_array_origin = self.angleLeft.sec_origin - nut_bolt_array_origin = nut_bolt_array_origin + self.angleLeft.T * self.angleLeft.uDir - nut_bolt_array_origin = nut_bolt_array_origin + self.angleLeft.A * self.angleLeft.vDir - - gauge_dir = self.angleLeft.vDir - pitch_dir = self.angleLeft.wDir - bolt_dir = -self.angleLeft.uDir - - # c_nutbolt_array_origin = self.angle.sec_origin - # c_nutbolt_array_origin = c_nutbolt_array_origin + self.angle.T * self.angle.uDir - # c_nutbolt_array_origin = c_nutbolt_array_origin + self.angle.B * self.angle.wDir - # - # c_gauge_dir = self.angle.wDir - # c_pitch_dir = self.angle.vDir - # c_bolt_dir = -self.angle.uDir - c_nutbolt_array_origin = self.angle.sec_origin - c_nutbolt_array_origin = c_nutbolt_array_origin + self.angle.T * self.angle.vDir - c_nutbolt_array_origin = c_nutbolt_array_origin + self.angle.B * self.angle.uDir - - c_gauge_dir = self.angle.uDir - c_pitch_dir = self.angle.wDir - c_bolt_dir = -self.angle.vDir - - # c_nutbolt_array_origin1 = self.angleLeft.sec_origin - # c_nutbolt_array_origin1 = c_nutbolt_array_origin1 + self.angle.T * self.angle.uDir - # c_nutbolt_array_origin1 = c_nutbolt_array_origin - (self.beam.t + self.angle.B) * self.angle.wDir - # c_nutbolt_array_origin1 = c_nutbolt_array_origin1 + (self.angle.L * self.angle.vDir) - # - # c_gauge_dir1 = self.angle.wDir - # c_pitch_dir1 = self.angle.vDir - # c_bolt_dir1 = -self.angle.uDir - c_nutbolt_array_origin1 = self.angleLeft.sec_origin - c_nutbolt_array_origin1 = c_nutbolt_array_origin1 + self.angle.T * self.angle.vDir - c_nutbolt_array_origin1 = c_nutbolt_array_origin - (self.beam.t + self.angle.B) * self.angle.uDir - c_nutbolt_array_origin1 = c_nutbolt_array_origin1 + (self.angle.L * self.angle.wDir) - - c_gauge_dir1 = self.angle.uDir - c_pitch_dir1 = self.angle.wDir - c_bolt_dir1 = -self.angle.vDir - - self.nut_bolt_array.place(nut_bolt_array_origin, -gauge_dir, pitch_dir, bolt_dir, c_nutbolt_array_origin, -c_gauge_dir, c_pitch_dir, c_bolt_dir, - c_nutbolt_array_origin1, c_gauge_dir1, c_pitch_dir1, c_bolt_dir1) - - def get_models(self): - '''Returning 3D models - ''' - return [self.columnModel, self.beamModel,self.angleModel,self.angleLeftModel] + self.nut_bolt_array.get_models() #[self.columnModel, self.beamModel, - - def get_nutboltmodels(self): - - return self.nut_bolt_array.get_models() - # return self.nut_bolt_array.getboltModels() - - def get_beamModel(self): - final_beam = self.beamModel - nut_bolt_list = self.nut_bolt_array.get_models() - for bolt in nut_bolt_list[0:(len(nut_bolt_list) // 2)]: - final_beam = BRepAlgoAPI_Cut(final_beam, bolt).Shape() - return final_beam - - - def get_columnModel(self): - final_beam = self.columnModel - nut_bolt_list = self.nut_bolt_array.get_models() - for bolt in nut_bolt_list[:]: - final_beam = BRepAlgoAPI_Cut(final_beam, bolt).Shape() - return final_beam diff --git a/cad/ShearConnections/CleatAngle/nutBoltPlacement.py b/cad/ShearConnections/CleatAngle/nutBoltPlacement.py deleted file mode 100644 index ac9f1ead3..000000000 --- a/cad/ShearConnections/CleatAngle/nutBoltPlacement.py +++ /dev/null @@ -1,208 +0,0 @@ -''' -Created on 07-Jun-2015 - -@author: deepa -''' -import numpy -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from .ModelUtils import get_gp_pt -import copy - - -class NutBoltArray(): - def __init__(self, bolt_place_obj, nut, bolt, nut_space, cnut_space): - self.origin = None - self.gauge_dir = None - self.pitch_dir = None - self.bolt_dir = None - - ################################# - self.c_origin = None - self.c_origin1 = None - self.c_gauge_dir = None - self.c_pitch_dir = None - self.c_bolt_dir = None - ############################################ - - self.init_bolt_place_params(bolt_place_obj) - - self.bolt = bolt - self.nut = nut - #self.gap = gap - self.gap = nut_space - - self.bolts = [] - self.nuts = [] - - self.positions = [] - ###################### - #self.cGap = cgap - self.cGap = cnut_space - self.cBolts = [] - self.cNuts = [] - self.cBolts1 = [] - self.cNuts1 = [] - ################################## - # self.calculate_positions() - self.initialise_nut_bolts() - - self.models = [] - - def initialise_nut_bolts(self): - b = self.bolt - n = self.nut - for i in range(self.row * self.col): - bolt_len_required = float(b.T + self.gap) - b.H = bolt_len_required + (5 - bolt_len_required) % 5 - self.bolts.append(Bolt(b.R, b.T, b.H, b.r)) - self.nuts.append(Nut(n.R, n.T, n.H, n.r1)) - # Newly added - for i in range(self.cRow * self.cCol): - bolt_len_required = float(b.T + self.cGap) - b.H = bolt_len_required + (5 - bolt_len_required) % 5 - self.cBolts.append(Bolt(b.R, b.T, b.H, b.r)) - self.cNuts.append(Nut(n.R, n.T, n.H, n.r1)) - for i in range(self.cRow * self.cCol): - bolt_len_required = float(b.T + self.cGap) - b.H = bolt_len_required + (5 - bolt_len_required) % 5 - self.cBolts1.append(Bolt(b.R, b.T, b.H, b.r)) - self.cNuts1.append(Nut(n.R, n.T, n.H, n.r1)) - - def init_bolt_place_params(self, bolt_place_obj): - self.pitch = bolt_place_obj.gauge_sptd - self.gauge = bolt_place_obj.pitch_sptd - self.edge = bolt_place_obj.edge_sptd - self.end = bolt_place_obj.end_sptd - self.row = bolt_place_obj.bolt_one_line_sptd - self.col = bolt_place_obj.bolt_lines_sptd - - -# ########changes have been made after 3d is integreted with main files#### - - self.cPitch = bolt_place_obj.gauge_spting - self.cGauge = bolt_place_obj.pitch_spting - self.cEdge = bolt_place_obj.edge_spting - self.cEnd = bolt_place_obj.end_spting - print(self.cEnd,"hghghwuuw") - self.cRow = bolt_place_obj.bolt_one_line_spting - self.cCol = bolt_place_obj.bolt_lines_spting - # self.thk = bolt_place_obj.cleat.thickness - self.leg = bolt_place_obj.leg_a_length - - def calculate_positions(self): - self.positions = [] - for rw in range(self.row): - for col in range(self.col): - pos = self.origin - pos = pos + (self.end)* self.gauge_dir - pos = pos + col * self.gauge * self.gauge_dir - pos = pos + self.edge * self.pitch_dir - pos = pos + rw * self.pitch * self.pitch_dir - - self.positions.append(pos) -# ###############Newly added###################### - self.cPositions = [] - for rw in range(self.cRow): - for col in range(self.cCol): - pos = self.c_origin - pos = pos + (self.cEnd) * self.c_gauge_dir - pos = pos + (col * self.cGauge * self.c_gauge_dir) - pos = pos + self.cEdge * self.c_pitch_dir - pos = pos + rw * self.cPitch * self.c_pitch_dir - - self.cPositions.append(pos) - self.cPositions1 = [] - for rw in range(self.cRow): - for col in range(self.cCol): - pos = self.c_origin1 - pos = pos + ((self.leg-self.cEnd) * self.c_gauge_dir) - pos = pos - col * self.cGauge * self.c_gauge_dir - pos = pos - self.cEdge * self.c_pitch_dir - pos = pos - rw * self.cPitch * self.c_pitch_dir - - self.cPositions1.append(pos) - - def place(self, origin, gauge_dir, pitch_dir, bolt_dir, c_origin, c_gauge_dir, c_pitch_dir, c_bolt_dir, c_origin1, c_gauge_dir1, c_pitch_dir1, c_bolt_dir1): - self.origin = origin - self.gauge_dir = gauge_dir - self.pitch_dir = pitch_dir - self.bolt_dir = bolt_dir - ###############Newly added#################### - self.c_origin = c_origin - self.c_gauge_dir = c_gauge_dir - self.c_pitch_dir = c_pitch_dir - self.c_bolt_dir = c_bolt_dir - - self.c_origin1 = c_origin1 - self.c_gauge_dir1 = c_gauge_dir1 - self.c_pitch_dir1 = c_pitch_dir1 - self.c_bolt_dir1 = c_bolt_dir1 - - ################################################ - - self.calculate_positions() - for index, pos in enumerate(self.positions): - self.bolts[index].place(pos, gauge_dir, bolt_dir) - self.nuts[index].place((pos + self.gap * bolt_dir), gauge_dir, -bolt_dir) - ###############Newly added#################### - for index, pos in enumerate(self.cPositions): - self.cBolts[index].place(pos, c_gauge_dir, c_bolt_dir) - self.cNuts[index].place((pos + self.cGap * c_bolt_dir), c_gauge_dir, -c_bolt_dir) - for index, pos in enumerate(self.cPositions1): - self.cBolts1[index].place(pos, c_gauge_dir1, c_bolt_dir1) - self.cNuts1[index].place((pos + self.cGap * c_bolt_dir1), c_gauge_dir1, -c_bolt_dir1) - - def create_model(self): - for bolt in self.bolts: - self.models.append(bolt.create_model()) - - for nut in self.nuts: - self.models.append(nut.create_model()) - ######################################### - for bolt in self.cBolts: - self.models.append(bolt.create_model()) - - for nut in self.cNuts: - self.models.append(nut.create_model()) - for bolt in self.cBolts1: - self.models.append(bolt.create_model()) - - for nut in self.cNuts1: - self.models.append(nut.create_model()) -# ################################################################ - dbg = self.dbg_sphere(self.origin) - self.models.append(dbg) - ######################################################################### - dbg1 = self.dbg_sphere(self.c_origin) - self.models.append(dbg1) - dbg2 = self.dbg_sphere(self.c_origin1) - self.models.append(dbg2) - - def dbg_sphere(self, pt): - return BRepPrimAPI_MakeSphere(get_gp_pt(pt), 0.1).Shape() - - def get_models(self): - return self.models - - def get_beambolts(self): - self.beambolts = [] - for bolt in self.bolts: - self.beambolts.append(bolt.create_model()) - dbg = self.dbg_sphere(self.origin) - self.beambolts.append(dbg) - return self.beambolts - - - def get_colbolts(self): - self.colbolts =[] - for bolt in self.cBolts: - self.colbolts.append(bolt.create_model()) - for bolt in self.cBolts1: - self.colbolts.append(bolt.create_model()) - dbg1 = self.dbg_sphere(self.c_origin) - self.colbolts.append(dbg1) - dbg2 = self.dbg_sphere(self.c_origin1) - self.colbolts.append(dbg2) - return self.colbolts \ No newline at end of file diff --git a/cad/ShearConnections/EndPlate/nutBoltPlacement.py b/cad/ShearConnections/EndPlate/nutBoltPlacement.py deleted file mode 100644 index a01de8417..000000000 --- a/cad/ShearConnections/EndPlate/nutBoltPlacement.py +++ /dev/null @@ -1,139 +0,0 @@ -''' -Created on 07-Jun-2015 - -@author: deepa -''' -import numpy as np -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from .ModelUtils import get_gp_pt -import copy - - -class NutBoltArray(): - def __init__(self, boltPlaceObj, bolt_place_obj, nut, bolt, nut_space): - self.origin = None - # self.origin1 = None - self.gauge_dir = None - self.pitch_dir = None - self.bolt_dir = None - - self.init_bolt_place_params(bolt_place_obj) - - self.bolt = bolt - self.nut = nut - self.gap = nut_space - - self.bolts = [] - self.nuts = [] - # self.bolts1 = [] - # self.nuts1 = [] - self.initialise_nut_bolts() - - self.positions = [] - # self.positions1 = [] - # self.calculate_positions() - - self.models = [] - - def initialise_nut_bolts(self): - b = self.bolt - n = self.nut - for i in np.arange(self.row * self.col): - bolt_len_required = float(b.T + self.gap) - b.H = bolt_len_required + (5 - bolt_len_required) % 5 - self.bolts.append(Bolt(b.R, b.T, b.H, b.r)) - self.nuts.append(Nut(n.R, n.T, n.H, n.r1)) - # for i in np.arange(self.row * self.col): - # bolt_len_required = float(b.T + self.gap) - # b.H = bolt_len_required + (5 - bolt_len_required) % 5 - # self.bolts1.append(Bolt(b.R, b.T, b.H, b.r)) - # self.nuts1.append(Nut(n.R, n.T, n.H, n.r1)) - - def init_bolt_place_params(self, plateObj): - self.pitch = plateObj.pitch_provided - self.gauge = plateObj.gauge_provided - self.edge = plateObj.edge_dist_provided - self.end = plateObj.end_dist_provided - self.row = plateObj.bolts_one_line - self.col = plateObj.bolt_line - self.sectional_gauge = plateObj.gauge_provided - # self.col = int(self.col / 2) - # self.row = 3 - # self.col = 2 - - def calculate_positions(self): - self.positions = [] - for rw in np.arange(self.row): - for col in np.arange(self.col): - pos = self.origin - pos = pos + (self.edge) * self.gauge_dir - pos = pos + col * self.gauge * self.gauge_dir - pos = pos + self.end * self.pitch_dir - pos = pos + rw * self.pitch * self.pitch_dir - - self.positions.append(pos) - # self.positions1 = [] - # for rw in np.arange(self.row): - # for col in np.arange(self.col): - # pos = self.origin1 - # pos = pos - (self.edge) * self.gauge_dir - # pos = pos - col * self.gauge * self.gauge_dir - # pos = pos + self.end * self.pitch_dir - # pos = pos + rw * self.pitch * self.pitch_dir - # - # self.positions1.append(pos) - - def place(self, origin, origin1, gauge_dir, pitch_dir, bolt_dir): - self.origin = origin - # self.origin1 = origin1 - self.gauge_dir = gauge_dir - self.pitch_dir = pitch_dir - self.bolt_dir = bolt_dir - - self.calculate_positions() - - for index, pos in enumerate(self.positions): - self.bolts[index].place(pos, gauge_dir, bolt_dir) - self.nuts[index].place((pos + self.gap * bolt_dir), gauge_dir, -bolt_dir) - # for index, pos in enumerate(self.positions1): - # self.bolts1[index].place(pos, gauge_dir, bolt_dir) - # self.nuts1[index].place((pos + self.gap * bolt_dir), gauge_dir, -bolt_dir) - - def create_model(self): - for bolt in self.bolts: - self.models.append(bolt.create_model()) - - for nut in self.nuts: - self.models.append(nut.create_model()) - - dbg = self.dbg_sphere(self.origin) - self.models.append(dbg) - - # for bolt in self.bolts1: - # self.models.append(bolt.create_model()) - - # for nut in self.nuts1: - # self.models.append(nut.create_model()) - # - # dbg1 = self.dbg_sphere(self.origin1) - # self.models.append(dbg1) - - def dbg_sphere(self, pt): - return BRepPrimAPI_MakeSphere(get_gp_pt(pt), 0.1).Shape() - - def get_models(self): - return self.models - - def get_bolt_list(self): - boltlist = [] - for bolt in self.bolts: - boltlist.append(bolt.create_model()) - dbg = self.dbg_sphere(self.origin) - self.models.append(dbg) - # for bolt in self.bolts1: - # boltlist.append(bolt.create_model()) - # dbg = self.dbg_sphere(self.origin1) - # self.models.append(dbg) - return boltlist \ No newline at end of file diff --git a/cad/ShearConnections/FinPlate/beamWebBeamWebConnectivity.py b/cad/ShearConnections/FinPlate/beamWebBeamWebConnectivity.py deleted file mode 100644 index ed79edf60..000000000 --- a/cad/ShearConnections/FinPlate/beamWebBeamWebConnectivity.py +++ /dev/null @@ -1,126 +0,0 @@ -''' -Created on 10-Mar-2016 - -@author: deepa -''' -import numpy -import copy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut -from design_type.connection import fin_plate_connection - - - -class BeamWebBeamWeb(): - - def __init__(self, column, beam, notch, plate, Fweld, nut_bolt_array,gap): - self.column = column - self.beam = beam - self.weldLeft = Fweld - self.weldRight = copy.deepcopy(Fweld) - self.plate = plate - self.nut_bolt_array = nut_bolt_array - self.notch = notch - self.gap = gap - self.columnModel = None - self.beamModel = None - self.weldModelLeft = None - self.weldModelRight = None - self.plateModel = None - # This distance between edge of the column web/flange and beam cross section - self.clearDist = 20.0 - - def create_3dmodel(self): - self.create_column_geometry() - self.create_beam_geometry() - self.create_plate_geometry() - self.create_fillet_weld_geometry() - self.create_nut_bolt_array() - - # Call for create_model - self.columnModel = self.column.create_model() - self.beamModel = self.beam.create_model() - self.plateModel = self.plate.create_model() - self.weldModelLeft = self.weldLeft.create_model() - self.weldModelRight = self.weldRight.create_model() - self.nutboltArrayModels = self.nut_bolt_array.create_model() - - def create_column_geometry(self): - columnOrigin = numpy.array([0, 0, 0]) - column_uDir = numpy.array([1.0, 0, 0]) - wDir1 = numpy.array([0.0, 1.0, 0.0]) - self.column.place(columnOrigin, column_uDir, wDir1) - - def create_beam_geometry(self): - uDir = numpy.array([0, 1.0, 0]) - wDir = numpy.array([1.0, 0, 0.0]) - shiftOrigin = (self.column.D / 2 - self.beam.D / 2) - origin2 = self.column.sec_origin + (-shiftOrigin) * self.column.vDir + \ - (self.column.t / 2 * self.column.uDir) + (self.column.length / 2 * self.column.wDir)\ - + (self.gap * self.column.uDir) - self.beam.place(origin2, uDir, wDir) - -# def createButtWeld(self): -# pass -# # plateThickness = 10 -# # uDir3 = numpy.array([0, 1.0, 0]) -# # wDir3 = numpy.array([1.0, 0, 0.0]) -# # origin3 = (self.column.sec_origin + -# # self.column.t/2.0 * self.column.uDir + -# # self.column.length/2.0 * self.column.wDir + -# # self.beam.t/2.0 * (-self.beam.uDir)+ -# # self.weld.W/2.0 * (-self.beam.uDir)) -# # #origin3 = numpy.array([0, 0, 500]) + t/2.0 *wDir3 + plateThickness/2.0 * (-self.beam.uDir) -# # self.weld.place(origin3, uDir3, wDir3) - - def create_plate_geometry(self): - plateOrigin = (self.beam.sec_origin + - (self.beam.D / 2 - self.notch.height) * self.beam.vDir + - self.beam.t / 2 * (-self.beam.uDir) + - self.plate.L / 2 * (-self.beam.vDir) + - self.plate.T / 2 * (-self.beam.uDir) + - self.gap * (-self.beam.wDir)) - uDir = numpy.array([0, 1.0, 0]) - wDir = numpy.array([1.0, 0, 0.0]) - self.plate.place(plateOrigin, uDir, wDir) - - def create_fillet_weld_geometry(self): - uDir = numpy.array([1.0, 0.0, 0]) - wDir = numpy.array([0.0, 0.0, 1.0]) - filletWeld1Origin = (self.plate.sec_origin + self.plate.T / 2.0 * self.weldLeft.vDir + self.weldLeft.L / 2.0 * (-self.weldLeft.wDir)) - self.weldLeft.place(filletWeld1Origin, uDir, wDir) - - uDir1 = numpy.array([0.0, -1.0, 0]) - wDir1 = numpy.array([0.0, 0.0, 1.0]) - filletWeld2Origin = (filletWeld1Origin + self.plate.T * (-self.weldLeft.vDir)) - self.weldRight.place(filletWeld2Origin, uDir1, wDir1) - - def create_nut_bolt_array(self): - - nutboltArrayOrigin = self.plate.sec_origin - nutboltArrayOrigin = nutboltArrayOrigin - self.plate.T / 2.0 * self.plate.uDir - nutboltArrayOrigin = nutboltArrayOrigin + self.plate.L / 2.0 * self.plate.vDir - - gaugeDir = self.plate.wDir - pitchDir = -self.plate.vDir - boltDir = self.plate.uDir - self.nut_bolt_array.place(nutboltArrayOrigin, gaugeDir, pitchDir, boltDir) - - def get_models(self): - '''Returning 3D models - ''' - return [self.columnModel, self.plateModel, self.weldModelLeft, self.weldModelRight, - self.beamModel] + self.nut_bolt_array.get_models() - - def get_nutboltmodels(self): - - return self.nut_bolt_array.get_models() - - def get_beamModel(self): - finalBeam = self.beamModel - nutBoltlist = self.nut_bolt_array.get_models() - for bolt in nutBoltlist[0:(len(nutBoltlist) // 2)]: - finalBeam = BRepAlgoAPI_Cut(finalBeam, bolt).Shape() - return finalBeam - - def get_columnModel(self): - return self.columnModel \ No newline at end of file diff --git a/cad/ShearConnections/FinPlate/nutBoltPlacement.py b/cad/ShearConnections/FinPlate/nutBoltPlacement.py deleted file mode 100644 index 7ab7d2678..000000000 --- a/cad/ShearConnections/FinPlate/nutBoltPlacement.py +++ /dev/null @@ -1,138 +0,0 @@ -''' -Created on 07-Jun-2015 - -@author: deepa -''' -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt - - -class NutBoltArray(): - - ''' - gDir - +----------------------------> - | - | - | P origin - | +-------+---------------+ - | | | | -pDir | | | End distance | - | | v | - | | X X | - | | | - | | | - | | | - v | | - | Gauge distance | - | X-------X | - | + | - | | | - | | Pitch | - | | | - | v | - | X X+----> + - | Edge distance - | | - | | - | | - +-----------------------+ - - Nut Bolt Placement - - ''' - - def __init__(self, boltPlaceObj, plateObj, nut, bolt, nut_space): - # finNutBoltArray(A.bolt, nut, bolt, nut_space) - - self.origin = None - self.gaugeDir = None - self.pitchDir = None - self.boltDir = None - - self.initBoltPlaceParams(plateObj) - - self.bolt = bolt - self.nut = nut - self.gap = nut_space - - self.bolts = [] - self.nuts = [] - self.initialiseNutBolts() - - self.positions = [] - # self.calculatePositions() - - self.models = [] - - def initialiseNutBolts(self): - ''' - Initializing the Nut and Bolt - ''' - b = self.bolt - n = self.nut - for i in range(self.row * self.col): - bolt_len_required = float(b.T + self.gap) - b.H = bolt_len_required + (5 - bolt_len_required) % 5 - self.bolts.append(Bolt(b.R, b.T, b.H, b.r)) - self.nuts.append(Nut(n.R, n.T, n.H, n.r1)) - - def initBoltPlaceParams(self, plateObj): - - self.pitch = plateObj.pitch_provided - self.gauge = plateObj.gauge_provided - self.edge = plateObj.end_dist_provided - self.plateedge = plateObj.end_dist_provided + plateObj.gap - self.end = plateObj.edge_dist_provided - self.row = plateObj.bolts_one_line - self.col = plateObj.bolt_line - - def calculatePositions(self): - ''' - Calculates the exact position for nuts and bolts. - ''' - self.positions = [] - for rw in range(self.col): - for col in range(self.row): - pos = self.origin - # pos = pos + self.end * self.gaugeDir - # #pos = pos + self.edge * self.gaugeDir - pos = pos + self.plateedge * self.gaugeDir - pos = pos + col * self.gauge * self.pitchDir - # pos = pos + self.edge * self.pitchDirself.gauge self.pitchDir - pos = pos + self.end * self.pitchDir - pos = pos + rw * self.pitch * self.gaugeDir - - self.positions.append(pos) - - def place(self, origin, gaugeDir, pitchDir, boltDir): - - self.origin = origin - self.gaugeDir = gaugeDir - self.pitchDir = pitchDir - self.boltDir = boltDir - - self.calculatePositions() - - for index, pos in enumerate(self.positions): - self.bolts[index].place(pos, gaugeDir, boltDir) - self.nuts[index].place((pos + self.gap * boltDir), gaugeDir, -boltDir) - - def create_model(self): - for bolt in self.bolts: - self.models.append(bolt.create_model()) - - for nut in self.nuts: - self.models.append(nut.create_model()) - - dbg = self.dbgSphere(self.origin) - self.models.append(dbg) - - def dbgSphere(self, pt): - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_models(self): - return self.models - diff --git a/cad/Tension/BoltedCAD.py b/cad/Tension/BoltedCAD.py deleted file mode 100644 index 799afc216..000000000 --- a/cad/Tension/BoltedCAD.py +++ /dev/null @@ -1,418 +0,0 @@ -""" -Initialized on 23-04-2020 -Comenced on -@author: Anand Swaroop -""" - -import numpy -import copy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut - - -class TensionAngleBoltCAD(object): - def __init__(self, Obj, member, plate, nut_bolt_array, intermittentConnection): - """ - :param member: Angle or Channel - :param plate: Plate - :param input: input parameters - :param memb_data: data of the members - """ - - self.Obj = Obj - self.member = member - self.plate = plate - self.nut_bolt_array = nut_bolt_array - self.intermittentConnection = intermittentConnection - - self.plate1 = copy.deepcopy(self.plate) - self.plate2 = copy.deepcopy(self.plate) - - self.member1 = copy.deepcopy(self.member) - self.member2 = copy.deepcopy(self.member) - # self.member2 = copy.deepcopy(self.member) - self.nut_bolt_arrayL = copy.deepcopy(self.nut_bolt_array) - self.nut_bolt_arrayR = copy.deepcopy(self.nut_bolt_array) - self.nut_bolt_arrayL_SA = copy.deepcopy(self.nut_bolt_array) - self.nut_bolt_arrayR_SA = copy.deepcopy(self.nut_bolt_array) - - # front side member - # weld vertical right side - - self.col = self.Obj.plate.bolt_line - self.end = self.Obj.plate.end_dist_provided - self.pitch = self.Obj.plate.pitch_provided - self.plate_intercept = 2 * self.end + (self.col - 1) * self.pitch - self.inter_length = self.member.L - 2*(self.end + (self.col -1) * self.pitch) - # print(self.inter_length) - - def create_3DModel(self): - - self.createMemberGeometry() - self.createPlateGeometry() - self.create_nut_bolt_array() - - def createMemberGeometry(self): - - if self.Obj.loc == 'Long Leg': - if self.Obj.sec_profile == 'Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, self.member.A / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj.sec_profile == 'Back to Back Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, self.member.A / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([self.member.L - self.plate_intercept, self.plate.T, self.member.A / 2]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - elif self.Obj.sec_profile == 'Star Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, 0.0]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([-self.plate_intercept, self.plate.T, 0.0]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - else: - if self.Obj.sec_profile == 'Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.member.B / 2]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj.sec_profile == 'Back to Back Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.member.B / 2]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([- self.plate_intercept, self.plate.T, self.member.B / 2]) - member2_uDir = numpy.array([0.0, 0.0, -1.0]) - member2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - elif self.Obj.sec_profile == 'Star Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, 0.0]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([-self.plate_intercept + self.member.L, self.plate.T, 0.0]) - member2_uDir = numpy.array([0.0, 0.0, 1.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - def createPlateGeometry(self): - plate1OriginL = numpy.array([0.0, 0.0, 0.0]) - plate1_uDir = numpy.array([1.0, 0.0, 0.0]) - plate1_wDir = numpy.array([0.0, 0.0, 1.0]) - self.plate1.place(plate1OriginL, plate1_uDir, plate1_wDir) - - self.plate1_Model = self.plate1.create_model() - - plate2OriginL = numpy.array([self.member.L - 2 * self.plate_intercept, self.plate.T, 0.0]) - plate2_uDir = numpy.array([-1.0, 0.0, 0.0]) - plate2_wDir = numpy.array([0.0, 0.0, 1.0]) - self.plate2.place(plate2OriginL, plate2_uDir, plate2_wDir) - - self.plate2_Model = self.plate2.create_model() - - if (self.Obj.sec_profile == 'Back to Back Angles' or self.Obj.sec_profile == 'Back to Back Channels' or self.Obj.sec_profile == 'Star Angles') and self.inter_length > 1000: - intermittentConnectionOriginL = numpy.array([0, 0.0, 0.0]) - intermittentConnection_uDir = numpy.array([0.0, 0.0, -1.0]) - intermittentConnection_vDir = numpy.array([1.0, 0.0, 0.0]) - intermittentConnection_wDir = numpy.array([0.0, 1.0, 0.0]) - self.intermittentConnection.place(intermittentConnectionOriginL, intermittentConnection_uDir, - intermittentConnection_vDir, intermittentConnection_wDir) - - self.intermittentConnection_Model = self.intermittentConnection.create_model() - self.inter_conc_bolts = self.intermittentConnection.get_nut_bolt_models() - self.inter_conc_plates = self.intermittentConnection.get_plate_models() - - def create_nut_bolt_array(self): - """ - - :return: Geometric Orientation of this component - """ - if self.Obj.sec_profile == 'Channels' or self.Obj.sec_profile == 'Back to Back Channels': - self.member.A = self.member.D - self.member.T = self.member.t - # nutboltArrayOrigin = self.baseplate.sec_origin + numpy.array([0.0, 0.0, self.baseplate.T /2+ 100]) - - if self.Obj.sec_profile == 'Star Angles': - nutboltArrayLOrigin = numpy.array([-self.plate_intercept, -self.member.T, 0.0]) - gaugeDir = numpy.array([0.0, 0, -1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 1.0, 0.0]) - self.nut_bolt_arrayL.place(nutboltArrayLOrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayLModels = self.nut_bolt_arrayL.create_model() - - nutboltArrayROrigin = numpy.array([self.member.L - 2 * self.plate_intercept, -self.member.T, 0.0]) - gaugeDir = numpy.array([0.0, 0, -1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 1.0, 0.0]) - self.nut_bolt_arrayR.place(nutboltArrayROrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayRModels = self.nut_bolt_arrayR.create_model() - - nutboltArrayL_SAOrigin = numpy.array([-self.plate_intercept, self.member.T + self.plate.T, 0.0]) - gaugeDir = numpy.array([0.0, 0, 1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, -1.0, 0.0]) - self.nut_bolt_arrayL_SA.place(nutboltArrayL_SAOrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayL_SAModels = self.nut_bolt_arrayL_SA.create_model() - - nutboltArrayR_SAOrigin = numpy.array( - [self.member.L - 2 * self.plate_intercept, self.member.T + self.plate.T, 0.0]) - gaugeDir = numpy.array([0.0, 0, 1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, -1.0, 0.0]) - self.nut_bolt_arrayR_SA.place(nutboltArrayR_SAOrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayR_SAModels = self.nut_bolt_arrayR_SA.create_model() - - else: - if self.Obj.sec_profile in ['Back to Back Angles', 'Angles']: - if self.Obj.loc == 'Long Leg': - self.placement = self.member.A / 2 - else: - self.placement = self.member.B / 2 - else: - self.placement = self.member.A/2 - nutboltArrayLOrigin = numpy.array([-self.plate_intercept, -self.member.T, self.placement]) - gaugeDir = numpy.array([0.0, 0, -1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 1.0, 0.0]) - self.nut_bolt_arrayL.place(nutboltArrayLOrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayLModels = self.nut_bolt_arrayL.create_model() - - nutboltArrayROrigin = numpy.array( - [-2 * self.plate_intercept + self.member.L, -self.member.T, self.placement]) - gaugeDir = numpy.array([0.0, 0, -1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 1.0, 0.0]) - self.nut_bolt_arrayR.place(nutboltArrayROrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayRModels = self.nut_bolt_arrayR.create_model() - - def get_members_models(self): - - if self.Obj.sec_profile == 'Angles': - member = self.member1_Model - - else: - member = BRepAlgoAPI_Fuse(self.member1_Model, self.member2_Model).Shape() - - return member - - def get_plates_models(self): - plate = BRepAlgoAPI_Fuse(self.plate1_Model, self.plate2_Model).Shape() - if (self.Obj.sec_profile == 'Back to Back Angles' or self.Obj.sec_profile == 'Back to Back Channels' or self.Obj.sec_profile == 'Star Angles') and self.inter_length > 1000: - plate = BRepAlgoAPI_Fuse(plate, self.inter_conc_plates).Shape() - return plate - - - def get_end_plates_models(self): - if self.Obj.sec_profile == 'Star Angles': - plate = BRepAlgoAPI_Fuse(self.plate1_Model,self.nutboltArrayLModels).Shape() - else: - plate = BRepAlgoAPI_Fuse(self.plate1_Model, self.nutboltArrayLModels).Shape() - - # if (self.Obj.sec_profile == 'Back to Back Angles' or self.Obj.sec_profile == 'Back to Back Channels' or self.Obj.sec_profile == 'Star Angles') and self.inter_length > 1000: - # plate = BRepAlgoAPI_Fuse(plate, self.inter_conc_plates).Shape() - return plate - - def get_nut_bolt_array_models(self): - - if self.Obj.sec_profile == 'Star Angles': - nut_bolts = [self.nutboltArrayLModels, self.nutboltArrayRModels, self.nutboltArrayL_SAModels, - self.nutboltArrayR_SAModels] - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - else: - array = BRepAlgoAPI_Fuse(self.nutboltArrayLModels, self.nutboltArrayRModels).Shape() - # array = nut_bolts[0] - # for comp in nut_bolts: - # array = BRepAlgoAPI_Fuse(comp, array).Shape() - - if (self.Obj.sec_profile == 'Back to Back Angles' or self.Obj.sec_profile == 'Back to Back Channels' or self.Obj.sec_profile == 'Star Angles') and self.inter_length > 1000: - array = BRepAlgoAPI_Fuse(array, self.inter_conc_bolts).Shape() - - return array - - def get_end_nut_bolt_array_models(self): - - if self.Obj.sec_profile == 'Star Angles': - nut_bolts = [self.nutboltArrayLModels, self.nutboltArrayL_SAModels] - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - else: - array = self.nutboltArrayLModels - - return array - - def get_only_members_models(self): - mem = self.get_members_models() - nut_bolts = self.get_nut_bolt_array_models() - - array = BRepAlgoAPI_Cut(mem, nut_bolts).Shape() - - return array - - def get_models(self): - mem = self.get_members_models() - plts = self.get_plates_models() - nut_bolts = self.get_nut_bolt_array_models() - - array = BRepAlgoAPI_Fuse(mem, plts).Shape() - - array = BRepAlgoAPI_Fuse(array, nut_bolts).Shape() - - return array - - -class TensionChannelBoltCAD(TensionAngleBoltCAD): - - def createMemberGeometry(self): - if self.Obj.sec_profile == 'Channels': - member1OriginL = numpy.array([-self.plate_intercept, -self.member.B, self.member.D / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj.sec_profile == 'Back to Back Channels': - member1OriginL = numpy.array([-self.plate_intercept, -self.member.B, self.member.D / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array( - [self.member.L - self.plate_intercept, self.plate.T + self.member.B, self.member.D / 2]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - def get_members_models(self): - - if self.Obj.sec_profile == 'Channels': - member = self.member1_Model - elif self.Obj.sec_profile == 'Back to Back Channels': - member = BRepAlgoAPI_Fuse(self.member1_Model, self.member2_Model).Shape() - - return member - - -if __name__ == '__main__': - import math - from cad.items.plate import Plate - from cad.items.channel import Channel - from cad.items.angle import Angle - from cad.items.bolt import Bolt - from cad.items.nut import Nut - from cad.items.stiffener_plate import StiffenerPlate - from cad.items.Gasset_plate import GassetPlate - from cad.Tension.nutBoltPlacement import NutBoltArray - - import OCC.Core.V3d - from OCC.Core.Quantity import Quantity_NOC_SADDLEBROWN, Quantity_NOC_BLUE1 - from OCC.Core.Graphic3d import Graphic3d_NOT_2D_ALUMINUM - from utilities import osdag_display_shape - # from cad.common_logic import CommonDesignLogic - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - member_data = 'Star Angles' # 'Back to Back Channels' #'Channels' #' #'Angles' # or 'Back to Back Angles' 'Channels' or - - # weld_size = 6 - # s = max(15, weld_size) - - bolt = Bolt(R=8, T=5, H=6, r=3) - nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - - if member_data == 'Channels' or member_data == 'Back to Back Channels': - member = Channel(B=50, T=6.6, D=125, t=3, R1=6.0, R2=2.4, L=4000) - plate = GassetPlate(L=360 + 50, H=205.0, T=16, degree=30) - # plate_intercept = plate.L - s - 50 - if member_data in ['Channels']: - nut_space = member.t + plate.T + nut.T # member.T + plate.T + nut.T - else: - nut_space = 2 * member.t + plate.T + nut.T # 2*member.T + plate.T + nut.T - plateObj = 0.0 - - nut_bolt_array = NutBoltArray(plateObj, nut, bolt, nut_space) - - tensionCAD = TensionChannelBoltCAD(member, plate, nut_bolt_array, member_data) - - - else: - member = Angle(L=2000.0, A=90.0, B=65.0, T=10.0, R1=8.0, R2=0.0) - plate = GassetPlate(L=295 + 50, H=120, T=10, degree=30) - # plate_intercept = plate.L - s - 50 - if member_data == 'Back to Back Angles': - nut_space = 2 * member.T + plate.T + nut.T # member.T + plate.T + nut.T - else: - nut_space = member.T + plate.T + nut.T # 2*member.T + plate.T + nut.T - - plateObj = 0.0 - - nut_bolt_array = NutBoltArray(plateObj, nut, bolt, nut_space) - - tensionCAD = TensionAngleBoltCAD(member, plate, nut_bolt_array, member_data) - - tensionCAD.create_3DModel() - plate = tensionCAD.get_plates_models() - mem = tensionCAD.get_members_models() - nutbolt = tensionCAD.get_nut_bolt_array_models() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(mem, update=True) - display.DisplayShape(plate, color='BLUE', update=True) - display.DisplayShape(nutbolt, color='YELLOW', update=True) - - start_display() diff --git a/cad/Tension/WeldedCAD.py b/cad/Tension/WeldedCAD.py deleted file mode 100644 index ed2a454e0..000000000 --- a/cad/Tension/WeldedCAD.py +++ /dev/null @@ -1,499 +0,0 @@ -""" -Initialized on 23-04-2020 -Comenced on -@author: Anand Swaroop -""" - -import numpy -import copy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse - -class TensionAngleWeldCAD(object): - def __init__(self, Obj, member, plate, inline_weld, opline_weld, weld_plate_array): - """ - :param member: Angle or Channel - :param plate: Plate - :param weld: weld - :param input: input parameters - :param memb_data: data of the members - """ - - self.Obj = Obj - self.member = member - self.plate = plate - self.inline_weld = inline_weld - self.opline_weld = opline_weld - self.intermittentConnection = weld_plate_array - - # self.Obj.loc = 'Long Leg'#'Short Leg' - - self.plate1 = copy.deepcopy(self.plate) - self.plate2 = copy.deepcopy(self.plate) - - self.member1 = copy.deepcopy(self.member) - self.member2 = copy.deepcopy(self.member) - # self.member2 = copy.deepcopy(self.member) - - # front side member - self.weldHL11 = copy.deepcopy(self.inline_weld) # weld horizontal left side 1 (top side of member) - self.weldHL12 = copy.deepcopy(self.inline_weld) # weld horzontal left side 2 (bottom side of member) - self.weldHR11 = copy.deepcopy(self.inline_weld) - self.weldHR12 = copy.deepcopy(self.inline_weld) - - self.weldVL11 = copy.deepcopy(self.opline_weld) # weld vertical left side - self.weldVR11 = copy.deepcopy(self.opline_weld) # weld vertical right side - - # for B2B members, back side member - self.weldHL21 = copy.deepcopy(self.inline_weld) # weld horizontal left side 1 (top side of member) - self.weldHL22 = copy.deepcopy(self.inline_weld) # weld horzontal left side 2 (bottom side of member) - self.weldHR21 = copy.deepcopy(self.inline_weld) - self.weldHR22 = copy.deepcopy(self.inline_weld) - - self.weldVL21 = copy.deepcopy(self.opline_weld) # weld vertical left side - self.weldVR21 = copy.deepcopy(self.opline_weld) # weld vertical right side - - self.s = max(15, self.inline_weld.h) - self.plate_intercept = self.plate.L - self.s - 50 - self.inter_length = self.member.L - 2*(self.plate.L - 50) - - def create_3DModel(self): - - self.createMemberGeometry() - self.createPlateGeometry() - self.createWeldGeometry() - - def createMemberGeometry(self): - - if self.Obj.loc == 'Long Leg': - if self.Obj.sec_profile == 'Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj.sec_profile == 'Back to Back Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array( - [self.member.L - self.plate_intercept, self.plate.T, self.opline_weld.L / 2]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - elif self.Obj.sec_profile == 'Star Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, 0.0]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([-self.plate_intercept, self.plate.T, 0.0]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - else: - if self.Obj.sec_profile == 'Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj.sec_profile == 'Back to Back Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([- self.plate_intercept, self.plate.T, self.opline_weld.L / 2]) - member2_uDir = numpy.array([0.0, 0.0, -1.0]) - member2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - elif self.Obj.sec_profile == 'Star Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, 0.0]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([-self.plate_intercept + self.member.L, self.plate.T, 0.0]) - member2_uDir = numpy.array([0.0, 0.0, 1.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - def createPlateGeometry(self): - plate1OriginL = numpy.array([0.0, 0.0, 0.0]) - plate1_uDir = numpy.array([1.0, 0.0, 0.0]) - plate1_wDir = numpy.array([0.0, 0.0, 1.0]) - self.plate1.place(plate1OriginL, plate1_uDir, plate1_wDir) - - self.plate1_Model = self.plate1.create_model() - - plate2OriginL = numpy.array([self.member.L - 2 * self.plate_intercept, self.plate.T, 0.0]) - plate2_uDir = numpy.array([-1.0, 0.0, 0.0]) - plate2_wDir = numpy.array([0.0, 0.0, 1.0]) - self.plate2.place(plate2OriginL, plate2_uDir, plate2_wDir) - - self.plate2_Model = self.plate2.create_model() - - if (self.Obj.sec_profile== 'Back to Back Angles' or self.Obj.sec_profile== 'Back to Back Channels' or self.Obj.sec_profile== 'Star Angles') and self.inter_length > 1000: - intermittentConnectionOriginL = numpy.array([0, 0.0, 0.0]) - intermittentConnection_uDir = numpy.array([1.0, 0.0, 0.0]) - intermittentConnection_vDir = numpy.array([0.0, 1.0, 0.0]) - intermittentConnection_wDir = numpy.array([0.0, 0.0, 1.0]) - self.intermittentConnection.place(intermittentConnectionOriginL, intermittentConnection_uDir, - intermittentConnection_vDir, intermittentConnection_wDir) - - self.intermittentConnection_Model = self.intermittentConnection.create_model() - self.inter_conc_welds = self.intermittentConnection.get_welded_models() - self.inter_conc_plates = self.intermittentConnection.get_plate_models() - - def createWeldGeometry(self): - if self.Obj.sec_profile == 'Back to Back Angles' or self.Obj.sec_profile == 'Angles' or self.Obj.sec_profile == 'Channels' or self.Obj.sec_profile == 'Back to Back Channels': - weldHL11OriginL = numpy.array([-self.plate_intercept, 0.0, self.opline_weld.L / 2]) - weldHL11_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHL11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHL11.place(weldHL11OriginL, weldHL11_uDir, weldHL11_wDir) - - self.weldHL11_Model = self.weldHL11.create_model() - - weldHL12OriginL = numpy.array([0.0, 0.0, -self.opline_weld.L / 2]) - weldHL12_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHL12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHL12.place(weldHL12OriginL, weldHL12_uDir, weldHL12_wDir) - - self.weldHL12_Model = self.weldHL12.create_model() - - weldHR11OriginL = numpy.array([-2 * self.plate_intercept + self.member.L, 0.0, self.opline_weld.L / 2]) - weldHR11_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHR11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHR11.place(weldHR11OriginL, weldHR11_uDir, weldHR11_wDir) - - self.weldHR11_Model = self.weldHR11.create_model() - - weldHR12OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, -self.opline_weld.L / 2]) - weldHR12_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHR12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHR12.place(weldHR12OriginL, weldHR12_uDir, weldHR12_wDir) - - self.weldHR12_Model = self.weldHR12.create_model() - - weldVL11OriginL = numpy.array([-self.plate_intercept, 0.0, -self.opline_weld.L / 2]) - weldVL11_uDir = numpy.array([-1.0, 0.0, 0.0]) - weldVL11_wDir = numpy.array([0.0, 0.0, 1.0]) - self.weldVL11.place(weldVL11OriginL, weldVL11_uDir, weldVL11_wDir) - - self.weldVL11_Model = self.weldVL11.create_model() - - weldVR11OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.opline_weld.L / 2]) - weldVR11_uDir = numpy.array([1.0, 0.0, 0.0]) - weldVR11_wDir = numpy.array([0.0, 0.0, -1.0]) - self.weldVR11.place(weldVR11OriginL, weldVR11_uDir, weldVR11_wDir) - - self.weldVR11_Model = self.weldVR11.create_model() - - if self.Obj.sec_profile == 'Back to Back Angles' or self.Obj.sec_profile == 'Back to Back Channels': - weldHL21OriginL = numpy.array([0.0, self.plate.T, self.opline_weld.L / 2]) - weldHL21_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHL21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHL21.place(weldHL21OriginL, weldHL21_uDir, weldHL21_wDir) - - self.weldHL21_Model = self.weldHL21.create_model() - - weldHL22OriginL = numpy.array([- self.plate_intercept, self.plate.T, -self.opline_weld.L / 2]) - weldHL22_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHL22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHL22.place(weldHL22OriginL, weldHL22_uDir, weldHL22_wDir) - - self.weldHL22_Model = self.weldHL22.create_model() - - weldHR21OriginL = numpy.array( - [- self.plate_intercept + self.member.L, self.plate.T, self.opline_weld.L / 2]) - weldHR21_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHR21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHR21.place(weldHR21OriginL, weldHR21_uDir, weldHR21_wDir) - - self.weldHR21_Model = self.weldHR21.create_model() - - weldHR22OriginL = numpy.array( - [-2 * self.plate_intercept + self.member.L, self.plate.T, -self.opline_weld.L / 2]) - weldHR22_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHR22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHR22.place(weldHR22OriginL, weldHR22_uDir, weldHR22_wDir) - - self.weldHR22_Model = self.weldHR22.create_model() - - weldVL21OriginL = numpy.array([-self.plate_intercept, self.plate.T, self.opline_weld.L / 2]) - weldVL21_uDir = numpy.array([-1.0, 0.0, 0.0]) - weldVL21_wDir = numpy.array([0.0, 0.0, -1.0]) - self.weldVL21.place(weldVL21OriginL, weldVL21_uDir, weldVL21_wDir) - - self.weldVL21_Model = self.weldVL21.create_model() - - weldVR21OriginL = numpy.array( - [-self.plate_intercept + self.member.L, self.plate.T, -self.opline_weld.L / 2]) - weldVR21_uDir = numpy.array([1.0, 0.0, 0.0]) - weldVR21_wDir = numpy.array([0.0, 0.0, 1.0]) - self.weldVR21.place(weldVR21OriginL, weldVR21_uDir, weldVR21_wDir) - - self.weldVR21_Model = self.weldVR21.create_model() - - elif self.Obj.sec_profile == 'Star Angles': - - weldHL11OriginL = numpy.array([-self.plate_intercept, 0.0, 0.0]) - weldHL11_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHL11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHL11.place(weldHL11OriginL, weldHL11_uDir, weldHL11_wDir) - - self.weldHL11_Model = self.weldHL11.create_model() - - weldHL12OriginL = numpy.array([0.0, 0.0, -self.opline_weld.L]) - weldHL12_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHL12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHL12.place(weldHL12OriginL, weldHL12_uDir, weldHL12_wDir) - - self.weldHL12_Model = self.weldHL12.create_model() - - weldHR11OriginL = numpy.array([-2 * self.plate_intercept + self.member.L, 0.0, 0.0]) - weldHR11_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHR11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHR11.place(weldHR11OriginL, weldHR11_uDir, weldHR11_wDir) - - self.weldHR11_Model = self.weldHR11.create_model() - - weldHR12OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, -self.opline_weld.L]) - weldHR12_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHR12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHR12.place(weldHR12OriginL, weldHR12_uDir, weldHR12_wDir) - - self.weldHR12_Model = self.weldHR12.create_model() - - weldVL11OriginL = numpy.array([-self.plate_intercept, 0.0, -self.opline_weld.L]) - weldVL11_uDir = numpy.array([-1.0, 0.0, 0.0]) - weldVL11_wDir = numpy.array([0.0, 0.0, 1.0]) - self.weldVL11.place(weldVL11OriginL, weldVL11_uDir, weldVL11_wDir) - - self.weldVL11_Model = self.weldVL11.create_model() - - weldVR11OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, 0.0]) - weldVR11_uDir = numpy.array([1.0, 0.0, 0.0]) - weldVR11_wDir = numpy.array([0.0, 0.0, -1.0]) - self.weldVR11.place(weldVR11OriginL, weldVR11_uDir, weldVR11_wDir) - - self.weldVR11_Model = self.weldVR11.create_model() - - weldHL21OriginL = numpy.array([0.0, self.plate.T, self.opline_weld.L]) - weldHL21_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHL21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHL21.place(weldHL21OriginL, weldHL21_uDir, weldHL21_wDir) - - self.weldHL21_Model = self.weldHL21.create_model() - - weldHL22OriginL = numpy.array([- self.plate_intercept, self.plate.T, 0.0]) - weldHL22_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHL22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHL22.place(weldHL22OriginL, weldHL22_uDir, weldHL22_wDir) - - self.weldHL22_Model = self.weldHL22.create_model() - - weldHR21OriginL = numpy.array([-self.plate_intercept + self.member.L, self.plate.T, self.opline_weld.L]) - weldHR21_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHR21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHR21.place(weldHR21OriginL, weldHR21_uDir, weldHR21_wDir) - - self.weldHR21_Model = self.weldHR21.create_model() - - weldHR22OriginL = numpy.array([-2 * self.plate_intercept + self.member.L, self.plate.T, 0.0]) - weldHR22_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHR22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHR22.place(weldHR22OriginL, weldHR22_uDir, weldHR22_wDir) - - self.weldHR22_Model = self.weldHR22.create_model() - - weldVL21OriginL = numpy.array([-self.plate_intercept, self.plate.T, self.opline_weld.L]) - weldVL21_uDir = numpy.array([-1.0, 0.0, 0.0]) - weldVL21_wDir = numpy.array([0.0, 0.0, -1.0]) - self.weldVL21.place(weldVL21OriginL, weldVL21_uDir, weldVL21_wDir) - - self.weldVL21_Model = self.weldVL21.create_model() - - weldVR21OriginL = numpy.array([-self.plate_intercept + self.member.L, self.plate.T, 0.0]) - weldVR21_uDir = numpy.array([1.0, 0.0, 0.0]) - weldVR21_wDir = numpy.array([0.0, 0.0, 1.0]) - self.weldVR21.place(weldVR21OriginL, weldVR21_uDir, weldVR21_wDir) - - self.weldVR21_Model = self.weldVR21.create_model() - - - def get_members_models(self): - - if self.Obj.sec_profile == 'Angles': - member = self.member1_Model - - else: - member = BRepAlgoAPI_Fuse(self.member1_Model, self.member2_Model).Shape() - - return member - - def get_plates_models(self): - plate = BRepAlgoAPI_Fuse(self.plate1_Model, self.plate2_Model).Shape() - if (self.Obj.sec_profile == 'Back to Back Angles' or self.Obj.sec_profile == 'Back to Back Channels' or self.Obj.sec_profile == 'Star Angles') and self.inter_length > 1000: - plate = BRepAlgoAPI_Fuse(plate, self.inter_conc_plates).Shape() - return plate - - def get_end_plates_models(self): - plate = self.plate1_Model - # if (self.Obj.sec_profile == 'Back to Back Angles' or self.Obj.sec_profile == 'Back to Back Channels' or self.Obj.sec_profile == 'Star Angles') and self.inter_length > 1000: - # plate = BRepAlgoAPI_Fuse(plate, self.inter_conc_plates).Shape() - return plate - - - def get_welded_models(self): - - if self.Obj.sec_profile == 'Angles' or self.Obj.sec_profile == 'Channels': - welded_sec = [self.weldHL11_Model, self.weldHL12_Model, self.weldHR11_Model, self.weldHR12_Model, - self.weldVL11_Model, self.weldVR11_Model] - elif (self.Obj.sec_profile== 'Back to Back Angles' or self.Obj.sec_profile== 'Back to Back Channels' or self.Obj.sec_profile== 'Star Angles') and self.inter_length > 1000: - welded_sec = [self.weldHL11_Model, self.weldHL12_Model, self.weldHR11_Model, self.weldHR12_Model, - self.weldVL11_Model, self.weldVR11_Model, self.weldHL21_Model, self.weldHL22_Model, - self.weldHR21_Model, self.weldHR22_Model, self.weldVL21_Model, self.weldVR21_Model, - self.inter_conc_welds] - else: - welded_sec = [self.weldHL11_Model, self.weldHL12_Model, self.weldHR11_Model, self.weldHR12_Model, - self.weldVL11_Model, self.weldVR11_Model, self.weldHL21_Model, self.weldHL22_Model, - self.weldHR21_Model, self.weldHR22_Model, self.weldVL21_Model, self.weldVR21_Model] - welds = welded_sec[0] - for comp in welded_sec[1:]: - welds = BRepAlgoAPI_Fuse(comp, welds).Shape() - return welds - - def get_models(self): - mem = self.get_welded_models() - plts = self.get_plates_models() - wlds = self.get_welded_models() - - array = BRepAlgoAPI_Fuse(mem, plts).Shape() - - array = BRepAlgoAPI_Fuse(array, wlds).Shape() - - return array -class TensionChannelWeldCAD(TensionAngleWeldCAD): - - def createMemberGeometry(self): - if self.Obj.sec_profile == 'Channels': - member1OriginL = numpy.array([-self.plate_intercept, -self.member.B, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj.sec_profile == 'Back to Back Channels': - member1OriginL = numpy.array([-self.plate_intercept, -self.member.B, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array( - [self.member.L - self.plate_intercept, self.plate.T + self.member.B, self.opline_weld.L / 2]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - - def get_members_models(self): - - if self.Obj.sec_profile == 'Channels': - member = self.member1_Model - elif self.Obj.sec_profile == 'Back to Back Channels': - member = BRepAlgoAPI_Fuse(self.member1_Model, self.member2_Model).Shape() - - return member - - -if __name__ == '__main__': - import math - from cad.items.plate import Plate - from cad.items.filletweld import FilletWeld - from cad.items.channel import Channel - from cad.items.angle import Angle - from cad.items.stiffener_plate import StiffenerPlate - from cad.items.Gasset_plate import GassetPlate - - import OCC.Core.V3d - from OCC.Core.Quantity import Quantity_NOC_SADDLEBROWN, Quantity_NOC_BLUE1 - from OCC.Core.Graphic3d import Graphic3d_NOT_2D_ALUMINUM - from utilities import osdag_display_shape - # from cad.common_logic import CommonDesignLogic - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - member_data = 'Star Angles' # 'Back to Back Angles' #'Angles' #''Back to Back Channels'#'Channels' # # - loc = 'Long Leg' # 'Short Leg'# - weld_size = 6 - s = max(15, weld_size) - - if member_data == 'Channels' or member_data == 'Back to Back Channels': - member = Channel(B=75, T=10.2, D=175, t=6, R1=0, R2=0, L=5000) - plate = GassetPlate(L=560 + 50, H=210, T=16, degree=30) - plate_intercept = plate.L - s - 50 - inline_weld = FilletWeld(b=weld_size, h=weld_size, L=plate_intercept) - opline_weld = FilletWeld(b=weld_size, h=weld_size, L=member.D) - - tensionCAD = TensionChannelWeldCAD(member_data, member, plate, inline_weld, opline_weld) - - - else: - member = Angle(L=2000.0, A=70.0, B=20.0, T=5.0, R1=0.0, R2=0.0) - plate = GassetPlate(L=540 + 50, H=255, T=5, degree=30) - plate_intercept = plate.L - s - 50 - inline_weld = FilletWeld(b=weld_size, h=weld_size, L=plate_intercept) - if loc == 'Long Leg': - opline_weld = FilletWeld(b=weld_size, h=weld_size, L=member.A) - else: - opline_weld = FilletWeld(b=weld_size, h=weld_size, L=member.B) - - tensionCAD = TensionAngleWeldCAD(member_data, member, plate, inline_weld, opline_weld) - - tensionCAD.create_3DModel() - plate = tensionCAD.get_plates_models() - mem = tensionCAD.get_members_models() - welds = tensionCAD.get_welded_models() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(mem, update=True) - display.DisplayShape(plate, color='BLUE', update=True) - display.DisplayShape(welds, color='Red', update=True) - - start_display() diff --git a/cad/Tension/nutBoltPlacement.py b/cad/Tension/nutBoltPlacement.py deleted file mode 100644 index 84ca6af0b..000000000 --- a/cad/Tension/nutBoltPlacement.py +++ /dev/null @@ -1,163 +0,0 @@ -''' -Created on 19-April-2020 - -@author : Anand Swaroop -''' - -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse - - -class NutBoltArray(): - """ - add a diagram here - """ - - def __init__(self, plateObj, nut, bolt, nut_space): - - self.nut = nut - self.bolt = bolt - - self.origin = None - self.gaugeDir = None - self.pitchDir = None - self.boltDir = None - - self.gap = nut_space - - self.initBoltPlaceParams(plateObj) - - self.bolts = [] - self.nuts = [] - self.initialiseNutBolts() - - self.positions = [] - - self.models = [] - - if plateObj.sec_profile == 'Channels' or plateObj.sec_profile == 'Back to Back Channels': - self.member_thickness = plateObj.section_size_1.flange_thickness - else: - self.member_thickness = plateObj.section_size_1.thickness - self.root_radius = plateObj.section_size_1.root_radius - # print(self.root_radius,self.member_thickness,"rad and thk") - def initialiseNutBolts(self): - ''' - Initializing the Nut Bolt - ''' - b = self.bolt - n = self.nut - for i in range(self.row * self.col): - bolt_len_required = float(self.gap) - b.H = bolt_len_required + 10 - self.bolts.append(Bolt(b.R, b.T, b.H, b.r)) - self.nuts.append(Nut(n.R, n.T, n.H, n.r1)) - - def initBoltPlaceParams(self, plateObj): - - self.pitch = plateObj.plate.pitch_provided - self.gauge = plateObj.plate.gauge_provided - self.edge = plateObj.plate.edge_dist_provided - # print(self.edge,"edge") - self.end = plateObj.plate.end_dist_provided - self.row = plateObj.plate.bolts_one_line - self.col = plateObj.plate.bolt_line - - def calculatePositions(self): - """ - Calculates the exact position for nut and bolts. - """ - self.positions = [] - for rw in range(self.row): - for col in range(self.col): - pos = self.origin - pos = pos + (self.member_thickness + self.root_radius) * self.gaugeDir - pos = pos + self.edge * self.gaugeDir - pos = pos + col * self.pitch * self.pitchDir - pos = pos + self.end * self.pitchDir - pos = pos + rw * self.gauge * self.gaugeDir - - self.positions.append(pos) - - def place(self, origin, gaugeDir, pitchDir, boltDir): - - self.origin = origin - self.gaugeDir = gaugeDir - self.pitchDir = pitchDir - self.boltDir = boltDir - - self.calculatePositions() - - for index, pos in enumerate(self.positions): - self.bolts[index].place(pos, gaugeDir, boltDir) - self.nuts[index].place((pos + self.gap * boltDir), gaugeDir, -boltDir) - - def create_model(self): - for bolt in self.bolts: - self.models.append(bolt.create_model()) - - for nut in self.nuts: - self.models.append(nut.create_model()) - - dbg = self.dbgSphere(self.origin) - self.models.append(dbg) - - nut_bolts = self.models - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - - return array - - def dbgSphere(self, pt): - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_models(self): - nut_bolts = self.models - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - - return array - - -if __name__ == '__main__': - from cad.items.bolt import Bolt - from cad.items.nut import Nut - import numpy - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - nutboltArrayOrigin = numpy.array([0., 0., 0.]) - gaugeDir = numpy.array([0.0, 1.0, 0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 0, 1.0]) - - bolt = Bolt(R=6, T=5, H=6, r=3) - nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - nut_space = 10 + 5 + nut.T # member.T + plate.T + nut.T - plateObj = 0.0 - - nut_bolt_array = NutBoltArray(plateObj, nut, bolt, nut_space) - - place = nut_bolt_array.place(nutboltArrayOrigin, gaugeDir, pitchDir, boltDir) - - nut_bolt_array_Model = nut_bolt_array.create_model() - - array = nut_bolt_array.get_models() - # array = nut_bolts[0] - # for comp in nut_bolts: - # array = BRepAlgoAPI_Fuse(comp, array).Shape() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(array, update=True) - display.DisableAntiAliasing() - start_display() diff --git a/cad/Tension/standaloneCAD/BoltedCAD.py b/cad/Tension/standaloneCAD/BoltedCAD.py deleted file mode 100644 index 9f5863ebf..000000000 --- a/cad/Tension/standaloneCAD/BoltedCAD.py +++ /dev/null @@ -1,379 +0,0 @@ -""" -Initialized on 23-04-2020 -Comenced on -@author: Anand Swaroop -""" - -import numpy -import copy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse - - -class TensionAngleBoltCAD(object): - def __init__(self, Obj, member, plate, nut_bolt_array, intermittentConnection): - """ - :param member: Angle or Channel - :param plate: Plate - :param input: input parameters - :param memb_data: data of the members - """ - - - self.Obj = Obj # 'Star Angles' # 'Back to Back Channels' #'Channels' #' #'Angles' # or 'Back to Back Angles' 'Channels' or - self.loc = 'Long Leg' - self.member = member - self.plate = plate - self.nut_bolt_array = nut_bolt_array - self.intermittentConnection = intermittentConnection - - self.plate1 = copy.deepcopy(self.plate) - self.plate2 = copy.deepcopy(self.plate) - - self.member1 = copy.deepcopy(self.member) - self.member2 = copy.deepcopy(self.member) - # self.member2 = copy.deepcopy(self.member) - self.nut_bolt_arrayL = copy.deepcopy(self.nut_bolt_array) - self.nut_bolt_arrayR = copy.deepcopy(self.nut_bolt_array) - self.nut_bolt_arrayL_SA = copy.deepcopy(self.nut_bolt_array) - self.nut_bolt_arrayR_SA = copy.deepcopy(self.nut_bolt_array) - - # front side member - # weld vertical right side - - self.col = 2 # self.Obj.plate.bolt_line - self.end = 35 # self.Obj.plate.end_dist_provided - self.pitch = 45 # self.Obj.plate.pitch_provided - self.plate_intercept = 2 * self.end + (self.col - 1) * self.pitch - - def create_3DModel(self): - - self.createMemberGeometry() - self.createPlateGeometry() - self.create_nut_bolt_array() - - def createMemberGeometry(self): - - if self.loc == 'Long Leg': - if self.Obj == 'Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, self.member.A / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj == 'Back to Back Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, self.member.A / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([self.member.L - self.plate_intercept, self.plate.T, self.member.A / 2]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - elif self.Obj == 'Star Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, 0.0]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([-self.plate_intercept, self.plate.T, 0.0]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - else: - if self.Obj == 'Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.member.B / 2]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj == 'Back to Back Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.member.B / 2]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([- self.plate_intercept, self.plate.T, self.member.B / 2]) - member2_uDir = numpy.array([0.0, 0.0, -1.0]) - member2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - elif self.Obj == 'Star Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, 0.0]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([-self.plate_intercept + self.member.L, self.plate.T, 0.0]) - member2_uDir = numpy.array([0.0, 0.0, 1.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - def createPlateGeometry(self): - plate1OriginL = numpy.array([0.0, 0.0, 0.0]) - plate1_uDir = numpy.array([1.0, 0.0, 0.0]) - plate1_wDir = numpy.array([0.0, 0.0, 1.0]) - self.plate1.place(plate1OriginL, plate1_uDir, plate1_wDir) - - self.plate1_Model = self.plate1.create_model() - - plate2OriginL = numpy.array([self.member.L - 2 * self.plate_intercept, self.plate.T, 0.0]) - plate2_uDir = numpy.array([-1.0, 0.0, 0.0]) - plate2_wDir = numpy.array([0.0, 0.0, 1.0]) - self.plate2.place(plate2OriginL, plate2_uDir, plate2_wDir) - - self.plate2_Model = self.plate2.create_model() - - if self.Obj == 'Back to Back Angles' or self.Obj == 'Back to Back Channels' or self.Obj == 'Star Angles' and self.member.L >1000: - intermittentConnectionOriginL = numpy.array([0, 0.0, 0.0]) - intermittentConnection_uDir = numpy.array([0.0, 0.0, -1.0]) - intermittentConnection_vDir = numpy.array([1.0, 0.0, 0.0]) - intermittentConnection_wDir = numpy.array([0.0, 1.0, 0.0]) - self.intermittentConnection.place(intermittentConnectionOriginL, intermittentConnection_uDir, - intermittentConnection_vDir, intermittentConnection_wDir) - - self.intermittentConnection_Model = self.intermittentConnection.create_model() - self.inter_conc_bolts = self.intermittentConnection.get_nut_bolt_models() - self.inter_conc_plates = self.intermittentConnection.get_plate_models() - - def create_nut_bolt_array(self): - """ - - :return: Geometric Orientation of this component - """ - if self.Obj == 'Channels' or self.Obj == 'Back to Back Channels': - self.member.A = self.member.D - self.member.T = self.member.t - # nutboltArrayOrigin = self.baseplate.sec_origin + numpy.array([0.0, 0.0, self.baseplate.T /2+ 100]) - - if self.Obj == 'Star Angles': - nutboltArrayLOrigin = numpy.array([-self.plate_intercept, -self.member.T, 0.0]) - gaugeDir = numpy.array([0.0, 0, -1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 1.0, 0.0]) - self.nut_bolt_arrayL.place(nutboltArrayLOrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayLModels = self.nut_bolt_arrayL.create_model() - - nutboltArrayROrigin = numpy.array([self.member.L - 2 * self.plate_intercept, -self.member.T, 0.0]) - gaugeDir = numpy.array([0.0, 0, -1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 1.0, 0.0]) - self.nut_bolt_arrayR.place(nutboltArrayROrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayRModels = self.nut_bolt_arrayR.create_model() - - nutboltArrayL_SAOrigin = numpy.array([-self.plate_intercept, self.member.T + self.plate.T, 0.0]) - gaugeDir = numpy.array([0.0, 0, 1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, -1.0, 0.0]) - self.nut_bolt_arrayL_SA.place(nutboltArrayL_SAOrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayL_SAModels = self.nut_bolt_arrayL_SA.create_model() - - nutboltArrayR_SAOrigin = numpy.array( - [self.member.L - 2 * self.plate_intercept, self.member.T + self.plate.T, 0.0]) - gaugeDir = numpy.array([0.0, 0, 1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, -1.0, 0.0]) - self.nut_bolt_arrayR_SA.place(nutboltArrayR_SAOrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayR_SAModels = self.nut_bolt_arrayR_SA.create_model() - - else: - if self.loc == 'Long Leg': - self.placement = self.member.A / 2 - else: - self.placement = self.member.B / 2 - nutboltArrayLOrigin = numpy.array([-self.plate_intercept, -self.member.T, self.placement]) - gaugeDir = numpy.array([0.0, 0, -1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 1.0, 0.0]) - self.nut_bolt_arrayL.place(nutboltArrayLOrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayLModels = self.nut_bolt_arrayL.create_model() - - nutboltArrayROrigin = numpy.array( - [-2 * self.plate_intercept + self.member.L, -self.member.T, self.placement]) - gaugeDir = numpy.array([0.0, 0, -1.0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 1.0, 0.0]) - self.nut_bolt_arrayR.place(nutboltArrayROrigin, gaugeDir, pitchDir, boltDir) - - self.nutboltArrayRModels = self.nut_bolt_arrayR.create_model() - - def get_members_models(self): - - if self.Obj == 'Angles': - member = self.member1_Model - - else: - member = BRepAlgoAPI_Fuse(self.member1_Model, self.member2_Model).Shape() - - return member - - def get_plates_models(self): - plate = BRepAlgoAPI_Fuse(self.plate1_Model, self.plate2_Model).Shape() - if self.Obj == 'Back to Back Angles' or self.Obj == 'Back to Back Channels' or self.Obj == 'Star Angles' and self.member.L >= 1000: - plate = BRepAlgoAPI_Fuse(plate, self.inter_conc_plates).Shape() - return plate - - def get_nut_bolt_array_models(self): - - if self.Obj == 'Star Angles': - nut_bolts = [self.nutboltArrayLModels, self.nutboltArrayRModels, self.nutboltArrayL_SAModels, - self.nutboltArrayR_SAModels] - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - else: - array = BRepAlgoAPI_Fuse(self.nutboltArrayLModels, self.nutboltArrayRModels).Shape() - # array = nut_bolts[0] - # for comp in nut_bolts: - # array = BRepAlgoAPI_Fuse(comp, array).Shape() - - if self.Obj == 'Back to Back Angles' or self.Obj == 'Back to Back Channels' or self.Obj == 'Star Angles' and self.member.L >= 1000: - array = BRepAlgoAPI_Fuse(array, self.inter_conc_bolts).Shape() - - return array - - def get_models(self): - pass - - -class TensionChannelBoltCAD(TensionAngleBoltCAD): - - def createMemberGeometry(self): - if self.Obj == 'Channels': - member1OriginL = numpy.array([-self.plate_intercept, -self.member.B, self.member.D / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj == 'Back to Back Channels': - member1OriginL = numpy.array([-self.plate_intercept, -self.member.B, self.member.D / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array( - [self.member.L - self.plate_intercept, self.plate.T + self.member.B, self.member.D / 2]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - def get_members_models(self): - - if self.Obj == 'Channels': - member = self.member1_Model - elif self.Obj == 'Back to Back Channels': - member = BRepAlgoAPI_Fuse(self.member1_Model, self.member2_Model).Shape() - - return member - - -if __name__ == '__main__': - import math - from cad.items.plate import Plate - from cad.items.channel import Channel - from cad.items.angle import Angle - from cad.items.bolt import Bolt - from cad.items.nut import Nut - from cad.items.stiffener_plate import StiffenerPlate - from cad.items.Gasset_plate import GassetPlate - from cad.Tension.standaloneCAD.nutBoltPlacement import NutBoltArray - from cad.Tension.standaloneCAD.IntermittentConnections import IntermittentNutBoltPlateArray - - import OCC.Core.V3d - from OCC.Core.Quantity import Quantity_NOC_SADDLEBROWN, Quantity_NOC_BLUE1 - from OCC.Core.Graphic3d import Graphic3d_NOT_2D_ALUMINUM - from utilities import osdag_display_shape - # from cad.common_logic import CommonDesignLogic - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - Obj = 'Star Angles' #'Back to Back Channels' # 'Channels' #' #'Angles' # or 'Back to Back Angles' 'Channels' or - - # weld_size = 6 - # s = max(15, weld_size) - - plate = GassetPlate(L=360 + 50, H=205.0, T=10, degree=30) - bolt = Bolt(R=8, T=5, H=6, r=3) - nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - intermittentPlate = Plate(L= 2*125 , W=70, T=plate.T) - - if Obj == 'Channels' or Obj == 'Back to Back Channels': - member = Channel(B=50, T=6.6, D=125, t=3, R1=6.0, R2=2.4, L=4000) - # plate_intercept = plate.L - s - 50 - if Obj == 'Channels': - nut_space = member.t + plate.T + nut.T # member.T + plate.T + nut.T - else: - nut_space = 2 * member.t + plate.T + nut.T # 2*member.T + plate.T + nut.T - # plateObj = 0.0 - - nut_bolt_array = NutBoltArray(Obj, nut, bolt, nut_space) - intermittentConnection = IntermittentNutBoltPlateArray(Obj, nut, bolt, intermittentPlate, nut_space) - - tensionCAD = TensionChannelBoltCAD(Obj, member, plate, nut_bolt_array, intermittentConnection) - - - else: - member = Angle(L=2000.0, A=90.0, B=65.0, T=10.0, R1=8.0, R2=0.0) - plate = GassetPlate(L=295 + 50, H=120, T=10, degree=30) - # plate_intercept = plate.L - s - 50 - if Obj == 'Back to Back Angles': - nut_space = 2 * member.T + plate.T + nut.T # member.T + plate.T + nut.T - else: - nut_space = member.T + plate.T + nut.T # 2*member.T + plate.T + nut.T - - plateObj = 0.0 - - nut_bolt_array = NutBoltArray(plateObj, nut, bolt, nut_space) - intermittentConnection = IntermittentNutBoltPlateArray(Obj, nut, bolt, intermittentPlate, nut_space) - - tensionCAD = TensionAngleBoltCAD(Obj, member, plate, nut_bolt_array, intermittentConnection) - - tensionCAD.create_3DModel() - plate = tensionCAD.get_plates_models() - mem = tensionCAD.get_members_models() - nutbolt = tensionCAD.get_nut_bolt_array_models() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(mem, update=True) - display.DisplayShape(plate, color='BLUE', update=True) - display.DisplayShape(nutbolt, color='YELLOW', update=True) - - start_display() diff --git a/cad/Tension/standaloneCAD/WeldedCAD.py b/cad/Tension/standaloneCAD/WeldedCAD.py deleted file mode 100644 index 3b3bf5558..000000000 --- a/cad/Tension/standaloneCAD/WeldedCAD.py +++ /dev/null @@ -1,491 +0,0 @@ -""" -Initialized on 23-04-2020 -Comenced on -@author: Anand Swaroop -""" - -import numpy -import copy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse - - -class TensionAngleWeldCAD(object): - def __init__(self, Obj, member, plate, inline_weld, opline_weld, weld_plate_array): - """ - :param member: Angle or Channel - :param plate: Plate - :param weld: weld - :param input: input parameters - :param memb_data: data of the members - """ - - self.Obj = Obj - self.member = member - self.plate = plate - self.inline_weld = inline_weld - self.opline_weld = opline_weld - self.intermittentConnection = weld_plate_array - - self.loc = 'Long Leg' # 'Short Leg' - - self.plate1 = copy.deepcopy(self.plate) - self.plate2 = copy.deepcopy(self.plate) - - self.member1 = copy.deepcopy(self.member) - self.member2 = copy.deepcopy(self.member) - # self.member2 = copy.deepcopy(self.member) - - # front side member - self.weldHL11 = copy.deepcopy(self.inline_weld) # weld horizontal left side 1 (top side of member) - self.weldHL12 = copy.deepcopy(self.inline_weld) # weld horzontal left side 2 (bottom side of member) - self.weldHR11 = copy.deepcopy(self.inline_weld) - self.weldHR12 = copy.deepcopy(self.inline_weld) - - self.weldVL11 = copy.deepcopy(self.opline_weld) # weld vertical left side - self.weldVR11 = copy.deepcopy(self.opline_weld) # weld vertical right side - - # for B2B members, back side member - self.weldHL21 = copy.deepcopy(self.inline_weld) # weld horizontal left side 1 (top side of member) - self.weldHL22 = copy.deepcopy(self.inline_weld) # weld horzontal left side 2 (bottom side of member) - self.weldHR21 = copy.deepcopy(self.inline_weld) - self.weldHR22 = copy.deepcopy(self.inline_weld) - - self.weldVL21 = copy.deepcopy(self.opline_weld) # weld vertical left side - self.weldVR21 = copy.deepcopy(self.opline_weld) # weld vertical right side - - self.s = max(15, self.inline_weld.h) - self.plate_intercept = self.plate.L - self.s - 50 - - def create_3DModel(self): - - self.createMemberGeometry() - self.createPlateGeometry() - self.createWeldGeometry() - - def createMemberGeometry(self): - - if self.loc == 'Long Leg': - if self.Obj == 'Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj == 'Back to Back Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array( - [self.member.L - self.plate_intercept, self.plate.T, self.opline_weld.L / 2]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - elif self.Obj == 'Star Angles': - member1OriginL = numpy.array([-self.plate_intercept, 0.0, 0.0]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([-self.plate_intercept, self.plate.T, 0.0]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - else: - if self.Obj == 'Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj == 'Back to Back Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([- self.plate_intercept, self.plate.T, self.opline_weld.L / 2]) - member2_uDir = numpy.array([0.0, 0.0, -1.0]) - member2_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - elif self.Obj == 'Star Angles': - member1OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, 0.0]) - member1_uDir = numpy.array([0.0, 0.0, -1.0]) - member1_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array([-self.plate_intercept + self.member.L, self.plate.T, 0.0]) - member2_uDir = numpy.array([0.0, 0.0, 1.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - def createPlateGeometry(self): - plate1OriginL = numpy.array([0.0, 0.0, 0.0]) - plate1_uDir = numpy.array([1.0, 0.0, 0.0]) - plate1_wDir = numpy.array([0.0, 0.0, 1.0]) - self.plate1.place(plate1OriginL, plate1_uDir, plate1_wDir) - - self.plate1_Model = self.plate1.create_model() - - plate2OriginL = numpy.array([self.member.L - 2 * self.plate_intercept, self.plate.T, 0.0]) - plate2_uDir = numpy.array([-1.0, 0.0, 0.0]) - plate2_wDir = numpy.array([0.0, 0.0, 1.0]) - self.plate2.place(plate2OriginL, plate2_uDir, plate2_wDir) - - self.plate2_Model = self.plate2.create_model() - - if self.Obj == 'Back to Back Angles' or self.Obj == 'Back to Back Channels' or self.Obj == 'Star Angles' and self.member.L >=1000: - intermittentConnectionOriginL = numpy.array([0, 0.0, 0.0]) - intermittentConnection_uDir = numpy.array([1.0, 0.0, 0.0]) - intermittentConnection_vDir = numpy.array([0.0, 1.0, 0.0]) - intermittentConnection_wDir = numpy.array([0.0, 0.0, 1.0]) - self.intermittentConnection.place(intermittentConnectionOriginL, intermittentConnection_uDir, - intermittentConnection_vDir, intermittentConnection_wDir) - - self.intermittentConnection_Model = self.intermittentConnection.create_model() - self.inter_conc_welds = self.intermittentConnection.get_welded_models() - self.inter_conc_plates = self.intermittentConnection.get_plate_models() - - def createWeldGeometry(self): - if self.Obj == 'Back to Back Angles' or self.Obj == 'Angles' or self.Obj == 'Channels' or self.Obj == 'Back to Back Channels': - weldHL11OriginL = numpy.array([-self.plate_intercept, 0.0, self.opline_weld.L / 2]) - weldHL11_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHL11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHL11.place(weldHL11OriginL, weldHL11_uDir, weldHL11_wDir) - - self.weldHL11_Model = self.weldHL11.create_model() - - weldHL12OriginL = numpy.array([0.0, 0.0, -self.opline_weld.L / 2]) - weldHL12_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHL12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHL12.place(weldHL12OriginL, weldHL12_uDir, weldHL12_wDir) - - self.weldHL12_Model = self.weldHL12.create_model() - - weldHR11OriginL = numpy.array([-2 * self.plate_intercept + self.member.L, 0.0, self.opline_weld.L / 2]) - weldHR11_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHR11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHR11.place(weldHR11OriginL, weldHR11_uDir, weldHR11_wDir) - - self.weldHR11_Model = self.weldHR11.create_model() - - weldHR12OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, -self.opline_weld.L / 2]) - weldHR12_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHR12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHR12.place(weldHR12OriginL, weldHR12_uDir, weldHR12_wDir) - - self.weldHR12_Model = self.weldHR12.create_model() - - weldVL11OriginL = numpy.array([-self.plate_intercept, 0.0, -self.opline_weld.L / 2]) - weldVL11_uDir = numpy.array([-1.0, 0.0, 0.0]) - weldVL11_wDir = numpy.array([0.0, 0.0, 1.0]) - self.weldVL11.place(weldVL11OriginL, weldVL11_uDir, weldVL11_wDir) - - self.weldVL11_Model = self.weldVL11.create_model() - - weldVR11OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, self.opline_weld.L / 2]) - weldVR11_uDir = numpy.array([1.0, 0.0, 0.0]) - weldVR11_wDir = numpy.array([0.0, 0.0, -1.0]) - self.weldVR11.place(weldVR11OriginL, weldVR11_uDir, weldVR11_wDir) - - self.weldVR11_Model = self.weldVR11.create_model() - - if self.Obj == 'Back to Back Angles' or self.Obj == 'Back to Back Channels': - weldHL21OriginL = numpy.array([0.0, self.plate.T, self.opline_weld.L / 2]) - weldHL21_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHL21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHL21.place(weldHL21OriginL, weldHL21_uDir, weldHL21_wDir) - - self.weldHL21_Model = self.weldHL21.create_model() - - weldHL22OriginL = numpy.array([- self.plate_intercept, self.plate.T, -self.opline_weld.L / 2]) - weldHL22_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHL22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHL22.place(weldHL22OriginL, weldHL22_uDir, weldHL22_wDir) - - self.weldHL22_Model = self.weldHL22.create_model() - - weldHR21OriginL = numpy.array( - [- self.plate_intercept + self.member.L, self.plate.T, self.opline_weld.L / 2]) - weldHR21_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHR21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHR21.place(weldHR21OriginL, weldHR21_uDir, weldHR21_wDir) - - self.weldHR21_Model = self.weldHR21.create_model() - - weldHR22OriginL = numpy.array( - [-2 * self.plate_intercept + self.member.L, self.plate.T, -self.opline_weld.L / 2]) - weldHR22_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHR22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHR22.place(weldHR22OriginL, weldHR22_uDir, weldHR22_wDir) - - self.weldHR22_Model = self.weldHR22.create_model() - - weldVL21OriginL = numpy.array([-self.plate_intercept, self.plate.T, self.opline_weld.L / 2]) - weldVL21_uDir = numpy.array([-1.0, 0.0, 0.0]) - weldVL21_wDir = numpy.array([0.0, 0.0, -1.0]) - self.weldVL21.place(weldVL21OriginL, weldVL21_uDir, weldVL21_wDir) - - self.weldVL21_Model = self.weldVL21.create_model() - - weldVR21OriginL = numpy.array( - [-self.plate_intercept + self.member.L, self.plate.T, -self.opline_weld.L / 2]) - weldVR21_uDir = numpy.array([1.0, 0.0, 0.0]) - weldVR21_wDir = numpy.array([0.0, 0.0, 1.0]) - self.weldVR21.place(weldVR21OriginL, weldVR21_uDir, weldVR21_wDir) - - self.weldVR21_Model = self.weldVR21.create_model() - - elif self.Obj == 'Star Angles': - - weldHL11OriginL = numpy.array([-self.plate_intercept, 0.0, 0.0]) - weldHL11_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHL11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHL11.place(weldHL11OriginL, weldHL11_uDir, weldHL11_wDir) - - self.weldHL11_Model = self.weldHL11.create_model() - - weldHL12OriginL = numpy.array([0.0, 0.0, -self.opline_weld.L]) - weldHL12_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHL12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHL12.place(weldHL12OriginL, weldHL12_uDir, weldHL12_wDir) - - self.weldHL12_Model = self.weldHL12.create_model() - - weldHR11OriginL = numpy.array([-2 * self.plate_intercept + self.member.L, 0.0, 0.0]) - weldHR11_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHR11_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHR11.place(weldHR11OriginL, weldHR11_uDir, weldHR11_wDir) - - self.weldHR11_Model = self.weldHR11.create_model() - - weldHR12OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, -self.opline_weld.L]) - weldHR12_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHR12_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHR12.place(weldHR12OriginL, weldHR12_uDir, weldHR12_wDir) - - self.weldHR12_Model = self.weldHR12.create_model() - - weldVL11OriginL = numpy.array([-self.plate_intercept, 0.0, -self.opline_weld.L]) - weldVL11_uDir = numpy.array([-1.0, 0.0, 0.0]) - weldVL11_wDir = numpy.array([0.0, 0.0, 1.0]) - self.weldVL11.place(weldVL11OriginL, weldVL11_uDir, weldVL11_wDir) - - self.weldVL11_Model = self.weldVL11.create_model() - - weldVR11OriginL = numpy.array([-self.plate_intercept + self.member.L, 0.0, 0.0]) - weldVR11_uDir = numpy.array([1.0, 0.0, 0.0]) - weldVR11_wDir = numpy.array([0.0, 0.0, -1.0]) - self.weldVR11.place(weldVR11OriginL, weldVR11_uDir, weldVR11_wDir) - - self.weldVR11_Model = self.weldVR11.create_model() - - weldHL21OriginL = numpy.array([0.0, self.plate.T, self.opline_weld.L]) - weldHL21_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHL21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHL21.place(weldHL21OriginL, weldHL21_uDir, weldHL21_wDir) - - self.weldHL21_Model = self.weldHL21.create_model() - - weldHL22OriginL = numpy.array([- self.plate_intercept, self.plate.T, 0.0]) - weldHL22_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHL22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHL22.place(weldHL22OriginL, weldHL22_uDir, weldHL22_wDir) - - self.weldHL22_Model = self.weldHL22.create_model() - - weldHR21OriginL = numpy.array([-self.plate_intercept + self.member.L, self.plate.T, self.opline_weld.L]) - weldHR21_uDir = numpy.array([0.0, 0.0, 1.0]) - weldHR21_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.weldHR21.place(weldHR21OriginL, weldHR21_uDir, weldHR21_wDir) - - self.weldHR21_Model = self.weldHR21.create_model() - - weldHR22OriginL = numpy.array([-2 * self.plate_intercept + self.member.L, self.plate.T, 0.0]) - weldHR22_uDir = numpy.array([0.0, 0.0, -1.0]) - weldHR22_wDir = numpy.array([1.0, 0.0, 0.0]) - self.weldHR22.place(weldHR22OriginL, weldHR22_uDir, weldHR22_wDir) - - self.weldHR22_Model = self.weldHR22.create_model() - - weldVL21OriginL = numpy.array([-self.plate_intercept, self.plate.T, self.opline_weld.L]) - weldVL21_uDir = numpy.array([-1.0, 0.0, 0.0]) - weldVL21_wDir = numpy.array([0.0, 0.0, -1.0]) - self.weldVL21.place(weldVL21OriginL, weldVL21_uDir, weldVL21_wDir) - - self.weldVL21_Model = self.weldVL21.create_model() - - weldVR21OriginL = numpy.array([-self.plate_intercept + self.member.L, self.plate.T, 0.0]) - weldVR21_uDir = numpy.array([1.0, 0.0, 0.0]) - weldVR21_wDir = numpy.array([0.0, 0.0, 1.0]) - self.weldVR21.place(weldVR21OriginL, weldVR21_uDir, weldVR21_wDir) - - self.weldVR21_Model = self.weldVR21.create_model() - - - - def get_members_models(self): - - if self.Obj == 'Angles': - member = self.member1_Model - - else: - member = BRepAlgoAPI_Fuse(self.member1_Model, self.member2_Model).Shape() - - return member - - def get_plates_models(self): - plate = BRepAlgoAPI_Fuse(self.plate1_Model, self.plate2_Model).Shape() - if self.Obj == 'Back to Back Angles' or self.Obj == 'Back to Back Channels' or self.Obj == 'Star Angles' and self.member.L >= 1000: - plate = BRepAlgoAPI_Fuse(plate, self.inter_conc_plates).Shape() - return plate - - def get_welded_models(self): - - if self.Obj == 'Angles' or self.Obj == 'Channels': - welded_sec = [self.weldHL11_Model, self.weldHL12_Model, self.weldHR11_Model, self.weldHR12_Model, - self.weldVL11_Model, self.weldVR11_Model, self.inter_conc_welds] - if self.Obj == 'Back to Back Angles' or self.Obj == 'Back to Back Channels' or self.Obj == 'Star Angles' and self.member.L >= 1000: - welded_sec = [self.weldHL11_Model, self.weldHL12_Model, self.weldHR11_Model, self.weldHR12_Model, - self.weldVL11_Model, self.weldVR11_Model, self.weldHL21_Model, self.weldHL22_Model, - self.weldHR21_Model, self.weldHR22_Model, self.weldVL21_Model, self.weldVR21_Model, - self.inter_conc_welds] - else: - welded_sec = [self.weldHL11_Model, self.weldHL12_Model, self.weldHR11_Model, self.weldHR12_Model, - self.weldVL11_Model, self.weldVR11_Model, self.weldHL21_Model, self.weldHL22_Model, - self.weldHR21_Model, self.weldHR22_Model, self.weldVL21_Model, self.weldVR21_Model] - welds = welded_sec[0] - for comp in welded_sec[1:]: - welds = BRepAlgoAPI_Fuse(comp, welds).Shape() - return welds - - def get_models(self): - pass - - -class TensionChannelWeldCAD(TensionAngleWeldCAD): - - def createMemberGeometry(self): - if self.Obj == 'Channels': - member1OriginL = numpy.array([-self.plate_intercept, -self.member.B, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - elif self.Obj == 'Back to Back Channels': - member1OriginL = numpy.array([-self.plate_intercept, -self.member.B, self.opline_weld.L / 2]) - member1_uDir = numpy.array([0.0, -1.0, 0.0]) - member1_wDir = numpy.array([1.0, 0.0, 0.0]) - self.member1.place(member1OriginL, member1_uDir, member1_wDir) - - self.member1_Model = self.member1.create_model() - - member2OriginL = numpy.array( - [self.member.L - self.plate_intercept, self.plate.T + self.member.B, self.opline_weld.L / 2]) - member2_uDir = numpy.array([0.0, 1.0, 0.0]) - member2_wDir = numpy.array([-1.0, 0.0, 0.0]) - self.member2.place(member2OriginL, member2_uDir, member2_wDir) - - self.member2_Model = self.member2.create_model() - - def get_members_models(self): - - if self.Obj == 'Channels': - member = self.member1_Model - elif self.Obj == 'Back to Back Channels': - member = BRepAlgoAPI_Fuse(self.member1_Model, self.member2_Model).Shape() - - return member - - -if __name__ == '__main__': - import math - from cad.items.plate import Plate - from cad.items.filletweld import FilletWeld - from cad.items.channel import Channel - from cad.items.angle import Angle - from cad.items.stiffener_plate import StiffenerPlate - from cad.items.Gasset_plate import GassetPlate - from cad.Tension.standaloneCAD.IntermittentConnections import IntermittentWelds - - import OCC.Core.V3d - from OCC.Core.Quantity import Quantity_NOC_SADDLEBROWN, Quantity_NOC_BLUE1 - from OCC.Core.Graphic3d import Graphic3d_NOT_2D_ALUMINUM - from utilities import osdag_display_shape - # from cad.common_logic import CommonDesignLogic - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - member_data = 'Star Angles' #'Back to Back Channels'# #'Channels' # 'Back to Back Angles' #'Angles' #' - loc = 'Long Leg' # 'Short Leg'# - weld_size = 6 - s = max(15, weld_size) - - intermittentPlate = Plate(L=195, W=80, T=16) - welds = FilletWeld(h=5, b=5, L=intermittentPlate.W) - weld_plate_array = IntermittentWelds(member_data, welds, intermittentPlate) - - if member_data == 'Channels' or member_data == 'Back to Back Channels': - member = Channel(B=75, T=10.2, D=175, t=6, R1=0, R2=0, L=2000) - plate = GassetPlate(L=560 + 50, H=210, T=16, degree=30) - plate_intercept = plate.L - s - 50 - inline_weld = FilletWeld(b=weld_size, h=weld_size, L=plate_intercept) - opline_weld = FilletWeld(b=weld_size, h=weld_size, L=member.D) - - tensionCAD = TensionChannelWeldCAD(member_data, member, plate, inline_weld, opline_weld, weld_plate_array) - - - else: - member = Angle(L=2000.0, A=70.0, B=20.0, T=5.0, R1=0.0, R2=0.0) - plate = GassetPlate(L=540 + 50, H=255, T=16, degree=30) - plate_intercept = plate.L - s - 50 - inline_weld = FilletWeld(b=weld_size, h=weld_size, L=plate_intercept) - if loc == 'Long Leg': - opline_weld = FilletWeld(b=weld_size, h=weld_size, L=member.A) - else: - opline_weld = FilletWeld(b=weld_size, h=weld_size, L=member.B) - - tensionCAD = TensionAngleWeldCAD(member_data, member, plate, inline_weld, opline_weld, weld_plate_array) - - tensionCAD.create_3DModel() - plate = tensionCAD.get_plates_models() - mem = tensionCAD.get_members_models() - welds = tensionCAD.get_welded_models() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(mem, update=True) - display.DisplayShape(plate, color='BLUE', update=True) - display.DisplayShape(welds, color='Red', update=True) - - start_display() diff --git a/cad/Tension/standaloneCAD/nutBoltPlacement.py b/cad/Tension/standaloneCAD/nutBoltPlacement.py deleted file mode 100644 index 7dbc90a5a..000000000 --- a/cad/Tension/standaloneCAD/nutBoltPlacement.py +++ /dev/null @@ -1,176 +0,0 @@ -''' -Created on 19-April-2020 - -@author : Anand Swaroop -''' - -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere -from cad.items.ModelUtils import getGpPt -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse -from cad.items.plate import Plate - - -class NutBoltArray(): - """ - add a diagram here - """ - - def __init__(self, Obj, nut, bolt, nut_space): - - self.nut = nut - self.bolt = bolt - - self.Obj = Obj - - self.origin = None - self.gaugeDir = None - self.pitchDir = None - self.boltDir = None - - self.gap = nut_space - - self.initBoltPlaceParams() - - self.bolts = [] - self.nuts = [] - self.initialiseNutBolts() - - self.positions = [] - - self.models = [] - - # if Obj == 'Channels' or Obj == 'Back to Back Channels': - # self.member_thickness = #Obj.section_size_1.flange_thickness - # else: - # self.member_thickness = #Obj.section_size_1.thickness - - def initialiseNutBolts(self): - ''' - Initializing the Nut Bolt - ''' - b = self.bolt - n = self.nut - for i in range(self.row * self.col): - bolt_len_required = float(b.T + self.gap) - b.H = bolt_len_required + (bolt_len_required) % 5 - self.bolts.append(Bolt(b.R, b.T, b.H, b.r)) - self.nuts.append(Nut(n.R, n.T, n.H, n.r1)) - - def initBoltPlaceParams(self): - - self.pitch = 45 # Obj.plate.pitch_provided - self.gauge = 35 # Obj.plate.gauge_provided - self.edge = 35 # Obj.plate.edge_dist_provided - self.end = 35 # Obj.plate.end_dist_provided - self.row = 2 # Obj.plate.bolts_one_line - self.col = 2 # Obj.plate.bolt_line - self.memberdeepth = 125 - self.member_thickness = 6.6 - self.member_web_thickness = 3 - self.root_radius = 6 - - - def calculatePositions(self): - """ - Calculates the exact position for nut and bolts. - """ - self.positions = [] - for rw in range(self.row): - for col in range(self.col): - pos = self.origin - pos = pos + (self.member_thickness + self.root_radius) * self.gaugeDir - pos = pos + self.edge * self.gaugeDir - pos = pos + col * self.pitch * self.pitchDir - pos = pos + self.end * self.pitchDir - pos = pos + rw * self.gauge * self.gaugeDir - - self.positions.append(pos) - - def place(self, origin, gaugeDir, pitchDir, boltDir): - - self.origin = origin - self.gaugeDir = gaugeDir - self.pitchDir = pitchDir - self.boltDir = boltDir - - self.calculatePositions() - - for index, pos in enumerate(self.positions): - self.bolts[index].place(pos, gaugeDir, boltDir) - self.nuts[index].place((pos + self.gap * boltDir), gaugeDir, -boltDir) - - def create_model(self): - for bolt in self.bolts: - self.models.append(bolt.create_model()) - - for nut in self.nuts: - self.models.append(nut.create_model()) - - dbg = self.dbgSphere(self.origin) - self.models.append(dbg) - - nut_bolts = self.models - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - - return array - - def dbgSphere(self, pt): - return BRepPrimAPI_MakeSphere(getGpPt(pt), 0.1).Shape() - - def get_models(self): - nut_bolts = self.models - array = nut_bolts[0] - for comp in nut_bolts: - array = BRepAlgoAPI_Fuse(comp, array).Shape() - - return array - - -if __name__ == '__main__': - from cad.items.bolt import Bolt - from cad.items.nut import Nut - from cad.items.plate import Plate - import numpy - - from OCC.gp import gp_Pnt - from OCC.Display.SimpleGui import init_display - - display, start_display, add_menu, add_function_to_menu = init_display() - - nutboltArrayOrigin = numpy.array([0., 0., 0.]) - gaugeDir = numpy.array([0.0, 1.0, 0]) - pitchDir = numpy.array([1.0, 0.0, 0]) - boltDir = numpy.array([0, 0, 1.0]) - - bolt = Bolt(R=6, T=5, H=6, r=3) - nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - nut_space = 10 + 5 + nut.T # member.T + plate.T + nut.T - Obj = 'Star Angles' # 'Back to Back Channels' #'Channels' #' #'Angles' # or 'Back to Back Angles' 'Channels' or - - nut_bolt_array = NutBoltArray(Obj, nut, bolt, nut_space) - # - # intermittentPlate = Plate(L= 35+35+35, W=35+35, T = 10) - # nut_bolt_array = IntermittentNutBoltPlateArray(Obj, nut, bolt, intermittentPlate, nut_space) - # - # place = nut_bolt_array.place(nutboltArrayOrigin, gaugeDir, pitchDir, boltDir) - - nut_bolt_array_Model = nut_bolt_array.create_model() - - array = nut_bolt_array.get_models() - # nbarray = nut_bolt_array.get_nut_bolt_models() - # parray = nut_bolt_array.get_plate_models() - # array = nut_bolts[0] - # for comp in nut_bolts: - # array = BRepAlgoAPI_Fuse(comp, array).Shape() - - Point = gp_Pnt(0.0, 0.0, 0.0) - display.DisplayMessage(Point, "Origin") - - display.DisplayShape(array, color='YELLOW', update=True) - # display.DisplayShape(parray, color= 'BLUE', update=True) - display.DisableAntiAliasing() - start_display() diff --git a/cad/common_logic.py b/cad/common_logic.py deleted file mode 100644 index 9d80e09e7..000000000 --- a/cad/common_logic.py +++ /dev/null @@ -1,2431 +0,0 @@ -''' -Created on 18-Nov-2016 - -@author: deepa, -modified : Sourabh Das, Darshan Vishwakarma -''' - -# from utils.common.component import Bolt,Beam,Section,Angle,Plate,Nut,Column,Weld -from cad.items.notch import Notch -from cad.items.bolt import Bolt -from cad.items.nut import Nut -from cad.items.plate import Plate -from cad.items.washer import Washer -from cad.items.ISection import ISection -from cad.items.filletweld import FilletWeld -from cad.items.groove_weld import GrooveWeld -from cad.items.angle import Angle -from cad.items.anchor_bolt import AnchorBolt_A, AnchorBolt_B, AnchorBolt_Endplate -from cad.items.stiffener_plate import StiffenerPlate -from cad.items.grout import Grout -from cad.items.angle import Angle -from cad.items.channel import Channel -from cad.items.Gasset_plate import GassetPlate -from cad.items.stiffener_flange import Stiffener_flange -from cad.items.rect_hollow import RectHollow -from cad.items.circular_hollow import CircularHollow - -from cad.ShearConnections.FinPlate.beamWebBeamWebConnectivity import BeamWebBeamWeb as FinBeamWebBeamWeb -from cad.ShearConnections.FinPlate.colFlangeBeamWebConnectivity import ColFlangeBeamWeb as FinColFlangeBeamWeb -from cad.ShearConnections.FinPlate.colWebBeamWebConnectivity import ColWebBeamWeb as FinColWebBeamWeb -from cad.ShearConnections.FinPlate.nutBoltPlacement import NutBoltArray as finNutBoltArray - -from cad.ShearConnections.CleatAngle.beamWebBeamWebConnectivity import BeamWebBeamWeb as cleatBeamWebBeamWeb -from cad.ShearConnections.CleatAngle.colFlangeBeamWebConnectivity import ColFlangeBeamWeb as cleatColFlangeBeamWeb -from cad.ShearConnections.CleatAngle.colWebBeamWebConnectivity import ColWebBeamWeb as cleatColWebBeamWeb -from cad.ShearConnections.CleatAngle.nutBoltPlacement import NutBoltArray as cleatNutBoltArray - -from cad.ShearConnections.EndPlate.beamWebBeamWebConnectivity import BeamWebBeamWeb as EndBeamWebBeamWeb -from cad.ShearConnections.EndPlate.colFlangeBeamWebConnectivity import ColFlangeBeamWeb as EndColFlangeBeamWeb -from cad.ShearConnections.EndPlate.colWebBeamWebConnectivity import ColWebBeamWeb as EndColWebBeamWeb -from cad.ShearConnections.EndPlate.nutBoltPlacement import NutBoltArray as endNutBoltArray - -from cad.ShearConnections.SeatedAngle.CAD_col_web_beam_web_connectivity import ColWebBeamWeb as seatColWebBeamWeb -from cad.ShearConnections.SeatedAngle.CAD_col_flange_beam_web_connectivity import ColFlangeBeamWeb as seatColFlangeBeamWeb -from cad.ShearConnections.SeatedAngle.CAD_nut_bolt_placement import NutBoltArray as seatNutBoltArray -# from cad.ShearConnections.SeatedAngle.seat_angle_calc import SeatAngleCalculation - -from cad.BBCad.nutBoltPlacement_AF import NutBoltArray_AF -from cad.BBCad.nutBoltPlacement_BF import NutBoltArray_BF -from cad.BBCad.nutBoltPlacement_Web import NutBoltArray_Web -from cad.BBCad.BBCoverPlateBoltedCAD import BBCoverPlateBoltedCAD - -from cad.MomentConnections.BBSpliceCoverlateCAD.WeldedCAD import BBSpliceCoverPlateWeldedCAD -from cad.MomentConnections.BBEndplate.BBEndplate_cadFile import CADFillet -from cad.MomentConnections.BBEndplate.BBEndplate_cadFile import CADGroove -from cad.MomentConnections.BCEndplate.BCEndplate_cadfile import CADGroove as BCECADGroove -from cad.MomentConnections.BCEndplate.BCEndplate_cadfile import CADcolwebGroove - -from cad.MomentConnections.CCSpliceCoverPlateCAD.WeldedCAD import CCSpliceCoverPlateWeldedCAD -from cad.MomentConnections.CCSpliceCoverPlateCAD.BoltedCAD import CCSpliceCoverPlateBoltedCAD -from cad.MomentConnections.CCSpliceCoverPlateCAD.nutBoltPlacement_AF import NutBoltArray_AF as CCSpliceNutBolt_AF -from cad.MomentConnections.CCSpliceCoverPlateCAD.nutBoltPlacement_BF import NutBoltArray_BF as CCSpliceNutBolt_BF -from cad.MomentConnections.CCSpliceCoverPlateCAD.nutBoltPlacement_Web import NutBoltArray_Web as CCSpliceNutBolt_Web - -from cad.BasePlateCad.baseplateconnection import BasePlateCad, HollowBasePlateCad -from cad.BasePlateCad.nutBoltPlacement import NutBoltArray as bpNutBoltArray - -from cad.Tension.WeldedCAD import TensionAngleWeldCAD, TensionChannelWeldCAD -from cad.Tension.BoltedCAD import TensionAngleBoltCAD, TensionChannelBoltCAD -from cad.Tension.nutBoltPlacement import NutBoltArray as TNutBoltArray -from cad.Tension.intermittentConnections import IntermittentNutBoltPlateArray, IntermittentWelds - -from cad.MomentConnections.CCEndPlateCAD.CAD import CCEndPlateCAD -from cad.MomentConnections.CCEndPlateCAD.nutBoltPlacement import NutBoltArray as CEPNutBoltArray - -# from design_type.connection.fin_plate_connection import FinPlateConnection -# from design_type.connection.cleat_angle_connection import CleatAngleConnection -from design_type.connection.beam_cover_plate import BeamCoverPlate -# from design_type.connection.base_plate_connection import BasePlateConnection -from utilities import osdag_display_shape, DisplayMsg -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse -import copy - -from cad.BBCad.nutBoltPlacement_AF import NutBoltArray_AF -from cad.BBCad.nutBoltPlacement_BF import NutBoltArray_BF -from cad.BBCad.nutBoltPlacement_Web import NutBoltArray_Web -from cad.BBCad.BBCoverPlateBoltedCAD import BBCoverPlateBoltedCAD -from cad.MomentConnections.BBEndplate.BBE_nutBoltPlacement import BBENutBoltArray -from cad.MomentConnections.BCEndplate.BCE_nutBoltPlacement import BCE_NutBoltArray -from Common import * -from math import * - -# from Connections.Shear.Finplate.colWebBeamWebConnectivity import ColWebBeamWeb as finColWebBeamWeb -# from Connections.Shear.Endplate.colWebBeamWebConnectivity import ColWebBeamWeb as endColWebBeamWeb -# from Connections.Shear.cleatAngle.colWebBeamWebConnectivity import ColWebBeamWeb as cleatColWebBeamWeb -# from Connections.Shear.SeatedAngle.CAD_col_web_beam_web_connectivity import ColWebBeamWeb as seatColWebBeamWeb -# -# from Connections.Shear.Finplate.beamWebBeamWebConnectivity import BeamWebBeamWeb as finBeamWebBeamWeb -# from Connections.Shear.Endplate.beamWebBeamWebConnectivity import BeamWebBeamWeb as endBeamWebBeamWeb -# from Connections.Shear.cleatAngle.beamWebBeamWebConnectivity import BeamWebBeamWeb as cleatBeamWebBeamWeb -# -# from Connections.Shear.Finplate.colFlangeBeamWebConnectivity import ColFlangeBeamWeb as finColFlangeBeamWeb -# from Connections.Shear.Endplate.colFlangeBeamWebConnectivity import ColFlangeBeamWeb as endColFlangeBeamWeb -# from Connections.Shear.cleatAngle.colFlangeBeamWebConnectivity import ColFlangeBeamWeb as cleatColFlangeBeamWeb -# from Connections.Shear.SeatedAngle.CAD_col_flange_beam_web_connectivity import ColFlangeBeamWeb as seatColFlangeBeamWeb - -# from Connections.Shear.Finplate.finPlateCalc import finConn -# from Connections.Shear.Endplate.endPlateCalc import end_connection -# from Connections.Shear.cleatAngle.cleatCalculation import cleat_connection -# from Connections.Shear.SeatedAngle.seat_angle_calc import SeatAngleCalculation -# from Connections.Component.filletweld import FilletWeld -# from Connections.Component.plate import Plate -# from Connections.Component.bolt import Bolt -# from Connections.Component.nut import Nut -# from Connections.Component.notch import Notch -# from Connections.Component.ISection import ISection -# from Connections.Component.angle import Angle -# from Connections.Shear.Finplate.nutBoltPlacement import NutBoltArray as finNutBoltArray -# from Connections.Shear.Endplate.nutBoltPlacement import NutBoltArray as endNutBoltArray -# from Connections.Shear.cleatAngle.nutBoltPlacement import NutBoltArray as cleatNutBoltArray -# from Connections.Shear.SeatedAngle.CAD_nut_bolt_placement import NutBoltArray as seatNutBoltArray -# from utilities import osdag_display_shape - -from OCC.Core.gp import (gp_Vec, gp_Pnt, gp_Trsf, gp_OX, gp_OY, - gp_OZ, gp_XYZ, gp_Ax2, gp_Dir, gp_GTrsf, gp_Mat) -from OCC.Core.BRepBuilderAPI import (BRepBuilderAPI_MakeEdge, - BRepBuilderAPI_MakeVertex, - BRepBuilderAPI_MakeWire, - BRepBuilderAPI_MakeFace, BRepBuilderAPI_MakeEdge2d, - BRepBuilderAPI_Transform) - -import OCC.Core.V3d -from OCC.Core.Quantity import * -from OCC.Core.Graphic3d import * -from OCC.Core.Quantity import Quantity_NOC_GRAY25 as GRAY -# from OCC.Core.AIS import Erase -# from OCC.Display.OCCViewer import V3d_XposYnegZneg -from OCC.Core.TNaming import tnaming -import multiprocessing - -# from Connections.Shear.Finplate.drawing_2D import FinCommonData -# from Connections.Shear.Endplate.drawing_2D import EndCommonData -# from Connections.Shear.cleatAngle.drawing2D import cleatCommonData -# from Connections.Shear.SeatedAngle.drawing_2D import SeatCommonData -# -# from Connections.Shear.Finplate.reportGenerator import save_html as fin_save_html -# from Connections.Shear.Endplate.reportGenerator import save_html as end_save_html -# from Connections.Shear.cleatAngle.reportGenerator import save_html as cleat_save_html -# from Connections.Shear.SeatedAngle.design_report_generator import ReportGenerator -# ----------------------------------------- from reportGenerator import save_html - - -class CommonDesignLogic(object): - # --------------------------------------------- def __init__(self, **kwargs): - # -------------------------------------------- self.uiObj = kwargs[uiObj] - # ------------------------------ self.dictbeamdata = kwargs[dictbeamdata] - # -------------------------------- self.dictcoldata = kwargs[dictcoldata] - # ------------------------------------------------ self.loc = kwargs[loc] - # ------------------------------------ self.component = kwargs[component] - # ------------------------------------------ self.bolt_R = kwargs[bolt_R] - # ------------------------------------------ self.bolt_T = kwargs[bolt_T] - # ---------------------------------------- self.bolt_Ht = kwargs[bolt_Ht] - # -------------------------------------------- self.nut_T = kwargs[nut_T] - # ----------------------------------------- self.display =kwargs[display] - # --------------------------- self.resultObj = self.call_finCalculation() - # ------------------------------------------- self.connectivityObj = None - - - def __init__(self, display, folder, connection, mainmodule): - - self.display = display - self.mainmodule = mainmodule - self.connection = connection - print(self.connection) - - - self.connectivityObj = None - self.folder = folder - - - def get_notch_ht(self, PB_T, PB_R1, SB_T, SB_R1): - """ - Args: - PB_T: (Float)Flange thickness of Primary beam - PB_R1: (Float) Root radius of Primary beam - SB_T: (Float) Flange thickness of Secondary beam - SB_R1: (Float) Root radius of Secondary beam - - Returns: (Float)Height of the coping based on maximum of sectional properties of Primary beam and Secondary beam - - """ - notch_ht = max([PB_T, SB_T]) + max([PB_R1, SB_R1]) + max([(PB_T/2), (SB_T/2),10]) - - return notch_ht - - def boltHeadThick_Calculation(self, boltDia): - ''' - This routine takes the bolt diameter and return bolt head thickness as per IS:3757(1989) and IS:1364 (PART-1) : 2002 - - - bolt Head Dia - <--------> - __________ - | | | T = Thickness - |________| | - | | - | | - | | - - Note: The head thickness for diameter 72 has been assumed and not taken from the IS code - - ''' - boltHeadThick = {5: 3.5, 6: 4, 8: 5.3, 10: 6.4, 12: 7.5, 14: 8.8, 16: 10, 18: 11.5, 20: 12.5, 22: 14, 24: 15, - 27: 17, 30: 18.7, 33: 21, 36: 22.5, 39: 25, 42: 26, 45: 28, 48: 30, 52: 33, 56: 35, 60: 38, 64: 40, 72: 45} - return boltHeadThick[boltDia] - - def boltHeadDia_Calculation(self, boltDia): - ''' - This routine takes the bolt diameter and return bolt head diameter as per IS:3757(1989) and IS:1364 (PART-1) : 2002 - - bolt Head Dia - <--------> - __________ - | | - |________| - | | - | | - | | - - ''' - boltHeadDia = {5: 8, 6: 10, 8: 13, 10: 16, 12: 18, 14: 21, 16: 24, 18: 27, 20: 30, 22: 34, 24: 36, 27: 41, - 30: 46, 33: 50, 36: 55, 39: 60, 42: 65, 45: 70, 48: 75, 52: 80, 56: 85, 60: 90, 64: 95, 72: 110} - return boltHeadDia[boltDia] - - def boltLength_Calculation(self, boltDia): - ''' - This routine takes the bolt diameter and return bolt head diameter as per IS:3757(1985) - - bolt Head Dia - <--------> - __________ ______ - | | | - |________| | - | | | - | | | - | | | - | | | - | | | l= length - | | | - | | | - | | | - |__| ___|__ - - ''' - # boltHeadDia = {5: 40, 6: 40, 8: 40, 10: 40, 12: 40, 16: 50, 20: 50, 22: 50, 24: 50, 27: 60, 30: 65, 36: 75} - - ''' - This routine takes the bolt diameter and return bolt head diameter as per IS:1364 (PART-1) : 2002 - - __________ - | | - |________| ______ - | | | - | | | - | | | - | | | - | | | l= length - | | | - | | | - | | | - |__| ___|__ - - ''' - boltLength = {5: 25, 6: 30, 8: 40, 10: 45, 12: 50, 14: 60, 16: 65, 18: 70, 20: 80, 22: 90, 24: 90, 27: 100, - 30: 110, 33: 130, 36: 140, 39: 150, 42: 180, 45: 200, 48: 220, 52: 240, 56: 260, 60: 280, 64: 300, 72: 320} - - return boltLength[boltDia] - - @staticmethod - def nutThick_Calculation(boltDia): - ''' - Returns the thickness of the hexagon nut (Grade A and B) depending upon the nut diameter as per IS1364-3(2002) - Table 1 - - Note: The nut thk for 72 diameter is not available in IS code, however an approximated value is assumed. - 72 mm dia bolt is used in the base plate module. - ''' - - # nutDia = {5: 5, 6: 5.65, 8: 7.15, 10: 8.75, 12: 11.3, 16: 15, 20: 17.95, 22: 19.0, 24: 21.25, 27: 23, 30: 25.35, - # 36: 30.65} - - ''' - Returns the thickness of the nut depending upon the nut diameter as per IS1364-3(2002) - ''' - nutDia = {5: 4.7, 6: 5.2, 8: 6.8, 10: 8.4, 12: 10.8, 14: 12.8, 16: 14.8, 18: 15.8, 20: 18.0, 22: 19.4, 24: 21.5, 27: 23.8, 30: 25.6, - 33: 28.7, 36: 31, 39: 33.4, 42: 34.0, 45: 36, 48: 38.0, 52: 42, 56: 45.0, 60: 48, 64: 51.0, 72: 60.0} - - return nutDia[boltDia] - - - def create3DBeamWebBeamWeb(self): - '''self,uiObj,resultObj,dictbeamdata,dictcoldata): - creating 3d cad model with beam web beam web - - ''' - - A = self.module_class() - - if self.connection == KEY_DISP_FINPLATE: - # A = self.module_class() - # A = FinPlateConnection() - plate = Plate(L=A.plate.height, W=A.plate.length, T=A.plate.thickness_provided) - Fweld1 = FilletWeld(L=A.weld.length, b=A.weld.size, h=A.weld.size) - - elif self.connection == KEY_DISP_CLEATANGLE: - # A = CleatAngleConnection() - angle = Angle(L=A.cleat.height, A=A.cleat.leg_a_length, B=A.cleat.leg_b_length, T=A.cleat.thickness, - R1=A.cleat.root_radius, R2=A.cleat.toe_radius) - - elif self.connection == KEY_DISP_ENDPLATE: - # A = self.module_class() - plate = Plate(L=A.plate.height, W=A.plate.width, T=A.plate.thickness_provided) - Fweld1 = FilletWeld(L=A.plate.height, b=A.weld.size, h=A.weld.size) - - else: - pass - - bolt_dia = int(A.bolt.bolt_diameter_provided) - bolt_r = bolt_dia / 2.0 - bolt_R = self.boltHeadDia_Calculation(bolt_dia) / 2.0 - bolt_T = self.boltHeadThick_Calculation(bolt_dia) - bolt_Ht = self.boltLength_Calculation(bolt_dia) - nut_T = self.nutThick_Calculation(bolt_dia) # bolt_dia = nut_dia - nut_Ht = bolt_dia - notch_height = A.supported_section.notch_ht - notch_R1 = max([A.supporting_section.root_radius, A.supported_section.root_radius, 10]) - - ##### SECONDARY BEAM PARAMETERS ###### - - - # --Notch dimensions - if self.connection == KEY_DISP_FINPLATE: - gap = A.plate.gap - notchObj = Notch(R1=notch_R1, - height=notch_height, - # width= (pBeam_B/2.0 - (pBeam_tw/2.0 ))+ gap, - width=(A.supporting_section.flange_width / 2.0 - ( - A.supporting_section.web_thickness / 2.0 + gap)) + gap, - length=A.supported_section.flange_width) - - elif self.connection == KEY_DISP_CLEATANGLE: - gap = A.cleat.gap - notchObj = Notch(R1=notch_R1, - height=notch_height, - width=(A.supporting_section.flange_width / 2.0 - ( - A.supporting_section.web_thickness / 2.0 + gap)) + gap, - length=A.supported_section.flange_width) - # print(notch_R1,notch_height,(A.supporting_section.flange_width / 2.0 - - # (A.supporting_section.web_thickness / 2.0 + gap)) + gap, A.supported_section.flange_width) - - elif self.connection == KEY_DISP_ENDPLATE: - notchObj = Notch(R1=notch_R1, height=notch_height, - width=(A.supporting_section.flange_width / 2.0 - ( - A.supporting_section.web_thickness / 2.0 + A.plate.thickness_provided)) + A.plate.gap, - length=A.supported_section.flange_width) - - else: - pass - # column = ISectionold(B = 83, T = 14.1, D = 250, t = 11, R1 = 12, R2 = 3.2, alpha = 98, length = 1000) - # - # beam = ISectionold(B = 140, T = 16,D = 400,t = 8.9, R1 = 14, R2 = 7, alpha = 98,length = 500) - - supporting = ISection(B=A.supporting_section.flange_width, T=A.supporting_section.flange_thickness, - D=A.supporting_section.depth, t=A.supporting_section.web_thickness, - R1=A.supporting_section.root_radius, R2=A.supporting_section.toe_radius, - alpha=A.supporting_section.flange_slope, - length=1000, notchObj=None) - - supported = ISection(B=A.supported_section.flange_width, T=A.supported_section.flange_thickness, - D=A.supported_section.depth, - t=A.supported_section.web_thickness, R1=A.supported_section.root_radius, - R2=A.supported_section.toe_radius, - alpha=A.supported_section.flange_slope, length=500, notchObj=notchObj) - - # bolt = Bolt(R = bolt_R,T = bolt_T, H = 38.0, r = 4.0 ) - bolt = Bolt(R=bolt_R, T=bolt_T, H=bolt_Ht, r=bolt_r) - - # nut =Nut(R = bolt_R, T = 10.0, H = 11, innerR1 = 4.0, outerR2 = 8.3) - nut = Nut(R=bolt_R, T=nut_T, H=nut_Ht, innerR1=bolt_r) - - if self.connection == KEY_DISP_FINPLATE: # finBeamWebBeamWeb/endBeamWebBeamWeb - nut_space = A.supported_section.web_thickness + A.plate.thickness_provided + nut_T - nutBoltArray = finNutBoltArray(A.bolt, A.plate, nut, bolt, nut_space) - beamwebconn = FinBeamWebBeamWeb(supporting, supported, notchObj, plate, Fweld1, nutBoltArray, gap) - # column, beam, notch, plate, Fweld, nut_bolt_array - - elif self.connection == KEY_DISP_ENDPLATE: - nut_space = A.supporting_section.web_thickness + A.plate.thickness_provided + nut_T - nutBoltArray = endNutBoltArray(A.bolt, A.plate, nut, bolt, nut_space) - beamwebconn = EndBeamWebBeamWeb(supporting, supported, notchObj, Fweld1, plate, nutBoltArray) - - elif self.connection == KEY_DISP_CLEATANGLE: - # nut_space = sBeam_tw + 2 * cleat_thick + nut_T - # cnut_space = pBeam_tw + cleat_thick + nut_T - # nut_bolt_array = cleatNutBoltArray(self.resultObj, nut, bolt, nut_space, cnut_space) - # beamwebconn = cleatBeamWebBeamWeb(column, beam, notchObj, angle, nut_bolt_array,gap) - nut_space = A.supported_section.web_thickness + 2 * A.cleat.thickness + nut_T - cnut_space = A.supporting_section.web_thickness + A.cleat.thickness + nut_T - nut_bolt_array = cleatNutBoltArray(A.cleat, nut, bolt, nut_space, cnut_space) - beamwebconn = cleatBeamWebBeamWeb(supporting, supported, notchObj, angle, nut_bolt_array,gap) - - else: - pass - - beamwebconn.create_3dmodel() - - return beamwebconn - - def create3DColWebBeamWeb(self): - ''' - creating 3d cad model with column web beam web - - ''' - - A = self.module_class() - - # if self.connection == KEY_DISP_FINPLATE: - # A = self.module_class() - # A = FinPlateConnection() - if self.connection == KEY_DISP_CLEATANGLE: - # A = CleatAngleConnection() - angle = Angle(L=A.cleat.height, A=A.cleat.leg_a_length, B=A.cleat.leg_b_length, T=A.cleat.thickness, - R1=A.cleat.root_radius, R2=A.cleat.toe_radius) - - elif self.connection == KEY_DISP_SEATED_ANGLE: - angle = Angle(L=A.seated_angle.width, A=A.seated.leg_a_length, B=A.seated.leg_b_length, - T=A.seated.thickness, R1=A.seated.root_radius, R2=A.seated.toe_radius) - else: - pass - #### PLATE,BOLT,ANGLE AND NUT PARAMETERS ##### - - # if self.connection == "cleatAngle": - # cleat_length = self.resultObj['cleat']['height'] - # cleat_thick = float(self.dictangledata["t"]) - # cleat_legsizes = str(self.dictangledata["AXB"]) - # angle_A = int(cleat_legsizes.split('x')[0]) - # angle_B = int(cleat_legsizes.split('x')[1]) - # angle_r1 = float(str(self.dictangledata["R1"])) - # angle_r2 = float(str(self.dictangledata["R2"])) - # - # elif self.connection == 'SeatedAngle': - # seat_length = self.resultObj['SeatAngle']['Length (mm)'] - # seat_thick = float(self.dictangledata["t"]) - # seat_legsizes = str(self.dictangledata["AXB"]) - # seatangle_A = int(seat_legsizes.split('x')[0]) - # seatangle_B = int(seat_legsizes.split('x')[1]) - # seatangle_r1 = float(str(self.dictangledata["R1"])) - # seatangle_r2 = float(str(self.dictangledata["R2"])) - # - # topangle_length = self.resultObj['SeatAngle']['Length (mm)'] - # topangle_thick = float(self.dicttopangledata["t"]) - # top_legsizes = str(self.dicttopangledata["AXB"]) - # topangle_A = int(top_legsizes.split('x')[0]) - # topangle_B = int(top_legsizes.split('x')[1]) - # topangle_r1 = float(str(self.dicttopangledata["R1"])) - # topangle_r2 = float(str(self.dicttopangledata["R2"])) - # else: - # fillet_length = self.resultObj['Plate']['height'] - # fillet_thickness = str(self.uiObj['Weld']['Size (mm)']) - # plate_width = self.resultObj['Plate']['width'] - # plate_thick = str(self.uiObj['Plate']['Thickness (mm)']) - - bolt_dia = int(A.bolt.bolt_diameter_provided) - bolt_r = bolt_dia / 2.0 - bolt_R = self.boltHeadDia_Calculation(bolt_dia) / 2.0 - bolt_T = self.boltHeadThick_Calculation(bolt_dia) - bolt_Ht = self.boltLength_Calculation(bolt_dia) - nut_T = self.nutThick_Calculation(bolt_dia) # bolt_dia = nut_dia - nut_Ht = bolt_dia - # notch_height = A.supported_section.notch_ht - # notch_R1 = max([A.supporting_section.root_radius, A.supported_section.root_radius, 10]) - - if self.connection == KEY_DISP_CLEATANGLE: - gap = A.cleat.gap - # notchObj = Notch(R1=notch_R1, - # height=notch_height, - # width=(A.supporting_section.flange_width / 2.0 - ( - # A.supporting_section.web_thickness / 2.0 + gap)) + gap, - # length=A.supported_section.flange_width) - # print(notch_R1, notch_height, (A.supporting_section.flange_width / 2.0 - - # (A.supporting_section.web_thickness / 2.0 + gap)) + gap, - # A.supported_section.flange_width) - elif self.connection == KEY_DISP_SEATED_ANGLE: - gap = A.plate.gap - seatangle = Angle(L=A.seated_angle.width, A=A.seated.leg_a_length, B=A.seated.leg_b_length, #TODO:Check leg b length - T=A.seated.thickness, R1=A.seated.root_radius, R2=A.seated.toe_radius) - topclipangle = Angle(L=A.top_angle.width, A=A.top_angle.leg_a_length, B=A.top_angle.leg_b_length, - T=A.top_angle.thickness, R1=A.top_angle.root_radius, R2=A.top_angle.toe_radius) - - elif self.connection == KEY_DISP_ENDPLATE: - plate = Plate(L=A.plate.height, W=A.plate.width, T=A.plate.thickness_provided) - Fweld1 = FilletWeld(L=A.weld.length, b=A.weld.size, h=A.weld.size) - - else: - plate = Plate(L=A.plate.height, W=A.plate.length, T=A.plate.thickness_provided) - Fweld1 = FilletWeld(L=A.weld.length, b=A.weld.size, h=A.weld.size) - - supporting = ISection(B=A.supporting_section.flange_width, T=A.supporting_section.flange_thickness, - D=A.supporting_section.depth, t=A.supporting_section.web_thickness, - R1=A.supporting_section.root_radius, R2=A.supporting_section.toe_radius, - alpha=A.supporting_section.flange_slope, - length=max(1000, (500 + A.supported_section.depth)), notchObj=None) - supported = ISection(B=A.supported_section.flange_width, T=A.supported_section.flange_thickness, - D=A.supported_section.depth, - t=A.supported_section.web_thickness, R1=A.supported_section.root_radius, - R2=A.supported_section.toe_radius, - alpha=A.supported_section.flange_slope, length=500, notchObj=None) - - bolt = Bolt(R=bolt_R, T=bolt_T, H=bolt_Ht, r=bolt_r) - nut = Nut(R=bolt_R, T=nut_T, H=nut_Ht, innerR1=bolt_r) - - if self.connection == KEY_DISP_FINPLATE: # finColWebBeamWeb - gap = A.plate.gap - nut_space = A.supported_section.web_thickness + int(A.plate.thickness_provided) + nut_T - nutBoltArray = finNutBoltArray(A.bolt, A.plate, nut, bolt, nut_space) - colwebconn = FinColWebBeamWeb(supporting, supported, Fweld1, plate, nutBoltArray,gap) - - elif self.connection == KEY_DISP_ENDPLATE: - nut_space = A.supporting_section.web_thickness + int(A.plate.thickness_provided) + nut_T - nutBoltArray = endNutBoltArray(A.bolt, A.plate, nut, bolt, nut_space) - colwebconn = EndColWebBeamWeb(supporting, supported, Fweld1, plate, nutBoltArray) - - elif self.connection == KEY_DISP_CLEATANGLE: - # nut_space = beam_tw + 2 * cleat_thick + nut_T - # cnut_space = column_tw + cleat_thick + nut_T - # nut_bolt_array = cleatNutBoltArray(self.resultObj, nut, bolt, nut_space, cnut_space) - # colwebconn = cleatColWebBeamWeb(column, beam, angle, nut_bolt_array,gap) - nut_space = A.supported_section.web_thickness + 2 * A.cleat.thickness + nut_T - cnut_space = A.supporting_section.web_thickness + A.cleat.thickness + nut_T - nut_bolt_array = cleatNutBoltArray(A.cleat, nut, bolt, nut_space, cnut_space) - colwebconn = cleatColWebBeamWeb(supporting, supported, angle, nut_bolt_array, gap) - - else: - snut_space = A.supporting_section.web_thickness + A.seated.thickness + nut_T - sbnut_space = A.supported_section.flange_thickness + A.seated.thickness + nut_T - tnut_space = A.supported_section.flange_thickness + A.top_angle.thickness + nut_T - tbnut_space = A.supporting_section.web_thickness + A.top_angle.thickness + nut_T - - nutBoltArray = seatNutBoltArray(A.bolt, nut, bolt, snut_space, sbnut_space, tnut_space, tbnut_space) - colwebconn = seatColWebBeamWeb(supporting, supported, seatangle, topclipangle, nutBoltArray, gap) - - colwebconn.create_3dmodel() - return colwebconn - - def create3DColFlangeBeamWeb(self): - ''' - Creating 3d cad model with column flange beam web connection - - ''' - - A = self.module_class() - - if self.connection == KEY_DISP_FINPLATE: - # A = self.module_class() - # A = FinPlateConnection() - gap = A.plate.gap - elif self.connection == KEY_DISP_CLEATANGLE: - # A = CleatAngleConnection() - angle = Angle(L=A.cleat.height, A=A.cleat.leg_a_length, B=A.cleat.leg_b_length, T=A.cleat.thickness, - R1=A.cleat.root_radius, R2=A.cleat.toe_radius) - elif self.connection == KEY_DISP_SEATED_ANGLE: - angle = Angle(L=A.seated_angle.width, A=A.seated.leg_a_length, B=A.seated.leg_b_length, - T=A.seated.thickness, R1=A.seated.root_radius, R2=A.seated.toe_radius) - else: - pass - - bolt_dia = int(A.bolt.bolt_diameter_provided) - bolt_r = bolt_dia / 2.0 - bolt_R = self.boltHeadDia_Calculation(bolt_dia) / 2.0 - bolt_T = self.boltHeadThick_Calculation(bolt_dia) - bolt_Ht = self.boltLength_Calculation(bolt_dia) - nut_T = self.nutThick_Calculation(bolt_dia) # bolt_dia = nut_dia - nut_Ht = bolt_dia - # gap = A.plate.gap - # notch_height = A.supported_section.notch_ht - # notch_R1 = max([A.supporting_section.root_radius, A.supported_section.root_radius, 10]) - - if self.connection == KEY_DISP_CLEATANGLE: - gap = A.cleat.gap - # notchObj = Notch(R1=notch_R1, - # height=notch_height, - # width=(A.supporting_section.flange_width / 2.0 - ( - # A.supporting_section.web_thickness / 2.0 + gap)) + gap, - # length=A.supported_section.flange_width) - # print(notch_R1, notch_height, (A.supporting_section.flange_width / 2.0 - - # (A.supporting_section.web_thickness / 2.0 + gap)) + gap, - # A.supported_section.flange_width) - - elif self.connection == KEY_DISP_SEATED_ANGLE: - gap = A.plate.gap - seatangle = Angle(L=A.seated_angle.width, A=A.seated.leg_a_length, B=A.seated.leg_b_length, #TODO:Check leg b length - T=A.seated.thickness, R1=A.seated.root_radius, R2=A.seated.toe_radius) - topclipangle = Angle(L=A.top_angle.width, A=A.top_angle.leg_a_length, B=A.top_angle.leg_b_length, - T=A.top_angle.thickness, R1=A.top_angle.root_radius, R2=A.top_angle.toe_radius) - - elif self.connection == KEY_DISP_ENDPLATE: - plate = Plate(L=A.plate.height, W=A.plate.width, T=A.plate.thickness_provided) - Fweld1 = FilletWeld(L=A.weld.length, b=A.weld.size, h=A.weld.size) - else: - # plate = Plate(L= 300,W =100, T = 10) - plate = Plate(L=A.plate.height, W=A.plate.length, T=A.plate.thickness_provided) - - # Fweld1 = FilletWeld(L= 300,b = 6, h = 6) - Fweld1 = FilletWeld(L=A.weld.length, b=A.weld.size, h=A.weld.size) - - supported = ISection(B=A.supported_section.flange_width, T=A.supported_section.flange_thickness, - D=A.supported_section.depth, - t=A.supported_section.web_thickness, R1=A.supported_section.root_radius, - R2=A.supported_section.toe_radius, - alpha=A.supported_section.flange_slope, length=500, notchObj=None) - - supporting = ISection(B=A.supporting_section.flange_width, T=A.supporting_section.flange_thickness, - D=A.supporting_section.depth, t=A.supporting_section.web_thickness, - R1=A.supporting_section.root_radius, R2=A.supporting_section.toe_radius, - alpha=A.supporting_section.flange_slope, - length=max(1000, (500 + A.supported_section.depth)), notchObj=None) - - # bolt = Bolt(R = bolt_R,T = bolt_T, H = 38.0, r = 4.0 ) - bolt = Bolt(R=bolt_R, T=bolt_T, H=bolt_Ht, r=bolt_r) - - # nut =Nut(R = bolt_R, T = 10.0, H = 11, innerR1 = 4.0, outerR2 = 8.3) - nut = Nut(R=bolt_R, T=nut_T, H=nut_Ht, innerR1=bolt_r) - - if self.connection == KEY_DISP_FINPLATE: - nut_space = A.supported_section.web_thickness+ int(A.plate.thickness_provided) + nut_T - # nutBoltArray = finNutBoltArray(A, nut, bolt, nut_space) # finColFlangeBeamWeb - # colflangeconn = finColFlangeBeamWeb(column, beam, Fweld1, plate, nutBoltArray, gap) - - nutBoltArray = finNutBoltArray(A.bolt, A.plate, nut, bolt, nut_space) - colflangeconn = FinColFlangeBeamWeb(supporting, supported, Fweld1, plate, nutBoltArray,gap) - - elif self.connection == KEY_DISP_ENDPLATE: - nut_space = A.supporting_section.flange_thickness + int(A.plate.thickness_provided) + nut_T - nutBoltArray = endNutBoltArray(A.bolt, A.plate, nut, bolt, nut_space) - colflangeconn = EndColFlangeBeamWeb(supporting, supported, Fweld1, plate, nutBoltArray) - - elif self.connection == KEY_DISP_CLEATANGLE: - - # nut_space = A.supported_section.web_thickness + 2 * + nut_T - # cnut_space = column_T + cleat_thick + nut_T - # nut_bolt_array = cleatNutBoltArray(self.resultObj, nut, bolt, nut_space, cnut_space) - # colflangeconn = cleatColFlangeBeamWeb(column, beam, angle, nut_bolt_array,gap) - nut_space = A.supported_section.web_thickness + 2 * A.cleat.thickness + nut_T - cnut_space = A.supporting_section.flange_thickness + A.cleat.thickness + nut_T - nut_bolt_array = cleatNutBoltArray(A.cleat, nut, bolt, nut_space, cnut_space) - colflangeconn = cleatColFlangeBeamWeb(supporting, supported, angle, nut_bolt_array, gap) - - else: - # pass - snut_space = A.supporting_section.flange_thickness + A.seated.thickness + nut_T - sbnut_space = A.supported_section.flange_thickness + A.seated.thickness + nut_T - tnut_space = A.supported_section.flange_thickness + A.top_angle.thickness + nut_T - tbnut_space = A.supporting_section.flange_thickness + A.top_angle.thickness + nut_T - - nutBoltArray = seatNutBoltArray(A.bolt, nut, bolt, snut_space, sbnut_space, tnut_space, tbnut_space, True) - colflangeconn = seatColFlangeBeamWeb(supporting, supported, seatangle, topclipangle, nutBoltArray, gap) - # - - # else: - # snut_space = column_T + seat_thick + nut_T - # sbnut_space = beam_T + seat_thick + nut_T - # tnut_space = beam_T + topangle_thick + nut_T - # tbnut_space = column_T + topangle_thick + nut_T - # - # nutBoltArray = seatNutBoltArray(self.resultObj, nut, bolt, snut_space, sbnut_space, tnut_space, tbnut_space) - # colflangeconn = seatColFlangeBeamWeb(column, beam, seatangle, topclipangle, nutBoltArray,gap) - - colflangeconn.create_3dmodel() - return colflangeconn - - def createBBCoverPlateCAD(self): - ''' - :return: The calculated values/parameters to create 3D CAD model of individual components. - ''' - - if self.connection == KEY_DISP_BEAMCOVERPLATE: - B = BeamCoverPlate() - # beam_data = self.fetchBeamPara() # Fetches the beam dimensions - - beam_tw = float(B.section.web_thickness) - beam_T = float(B.section.flange_thickness) - beam_d = float(B.section.depth) - beam_B = float(B.section.flange_width) - beam_R1 = float(B.section.root_radius) - beam_R2 = float(B.section.toe_radius) - beam_alpha = float(B.section.flange_slope) - beam_length = B.flange_plate.length/2+300 - - beam_Left = ISection(B=beam_B, T=beam_T, D=beam_d, t=beam_tw, - R1=beam_R1, R2=beam_R2, alpha=beam_alpha, - length=beam_length, notchObj=None) # Call to ISection in Component repository - beam_Right = copy.copy(beam_Left) # Since both the beams are same - - - plateAbvFlange = Plate(L=B.flange_plate.height, - W=B.flange_plate.length, - T=float(B.flange_plate.thickness_provided)) # Call to Plate in Component repository - plateBelwFlange = copy.copy(plateAbvFlange) # Since both the flange plates are identical - - innerplateAbvFlangeFront = Plate(L=B.flange_plate.Innerheight, - W=B.flange_plate.Innerlength, - T=float(B.flange_plate.thickness_provided)) - innerplateAbvFlangeBack = copy.copy(innerplateAbvFlangeFront) - innerplateBelwFlangeFront = copy.copy(innerplateAbvFlangeBack) - innerplateBelwFlangeBack = copy.copy(innerplateBelwFlangeFront) - - WebPlateLeft = Plate(L=B.web_plate.height, - W=B.web_plate.length, - T=float(B.web_plate.thickness_provided)) # Call to Plate in Component repository - WebPlateRight = copy.copy(WebPlateLeft) # Since both the Web plates are identical - - bolt_d = float(B.flange_bolt.bolt_diameter_provided) # Bolt diameter (shank part), entered by user - bolt_r = bolt_d / 2 # Bolt radius (Shank part) - bolt_T = self.boltHeadThick_Calculation(bolt_d) # Bolt head thickness - bolt_R = self.boltHeadDia_Calculation(bolt_d) / 2 # Bolt head diameter (Hexagon) - bolt_Ht = self.boltLength_Calculation(bolt_d) # Bolt head height - - bolt = Bolt(R=bolt_R, T=bolt_T, H=bolt_Ht, r=bolt_r) # Call to create Bolt from Component directory - nut_T = self.nutThick_Calculation(bolt_d) # Nut thickness, usually nut thickness = nut height - nut_Ht = nut_T - nut = Nut(R=bolt_R, T=nut_T, H=nut_Ht, innerR1=bolt_r) # Call to create Nut from Component directory - - numOfBoltsF = int(B.flange_plate.bolts_required) # Number of flange bolts for both beams - if B.preference == "Outside": - nutSpaceF = float( - B.flange_plate.thickness_provided) + beam_T # Space between bolt head and nut for flange bolts - else: - nutSpaceF = 2 * float(B.flange_plate.thickness_provided) + beam_T - - # TODO : update nutSpace from Osdag test - - numOfBoltsW = int(B.web_plate.bolts_required) # Number of web bolts for both beams - nutSpaceW = 2 * float( - B.web_plate.thickness_provided) + beam_tw # Space between bolt head and nut for web bolts - - # Bolt placement for Above Flange bolts, call to nutBoltPlacement_AF.py - bolting_AF = NutBoltArray_AF(BeamCoverPlate(), nut, bolt, numOfBoltsF, nutSpaceF) - - # Bolt placement for Below Flange bolts, call to nutBoltPlacement_BF.py - bolting_BF = NutBoltArray_BF(BeamCoverPlate(), nut, bolt, numOfBoltsF, nutSpaceF) - - # Bolt placement for Web Plate bolts, call to nutBoltPlacement_Web.py - bolting_Web = NutBoltArray_Web(BeamCoverPlate(), nut, bolt, numOfBoltsW, nutSpaceW) - - # bbCoverPlate is an object which is passed BBCoverPlateBoltedCAD.py file, which initialized the parameters of each CAD component - bbCoverPlate = BBCoverPlateBoltedCAD(beam_Left, beam_Right, plateAbvFlange, plateBelwFlange, - innerplateAbvFlangeFront, - innerplateAbvFlangeBack, innerplateBelwFlangeFront, - innerplateBelwFlangeBack, - WebPlateLeft, WebPlateRight, bolting_AF, bolting_BF, bolting_Web, - BeamCoverPlate()) - - # bbCoverPlate.create_3DModel() will create the CAD model of each component, debugging this line will give moe clarity - bbCoverPlate.create_3DModel() - - elif self.connection == KEY_DISP_BEAMCOVERPLATEWELD: - B = self.module_class() - beamLenght = (max(float(B.flange_plate.length), float(B.web_plate.length)) + 600) / 2 - beam = ISection(B=float(B.section.flange_width), T=float(B.section.flange_thickness), - D=float(B.section.depth), t=float(B.section.web_thickness), R1=float(B.section.root_radius), - R2=float(B.section.toe_radius), alpha=float(B.section.flange_slope), length=beamLenght, - notchObj=None) - flangePlate = Plate(L=float(B.flange_plate.length), W=float(B.flange_plate.height), - T=float(B.flange_plate.thickness_provided)) - innerFlangePlate = Plate(L=float(B.flange_plate.Innerlength), W=float(B.flange_plate.Innerheight), - T=float(B.flange_plate.thickness_provided)) - webPlate = Plate(L=float(B.web_plate.length), W=float(B.web_plate.height), - T=float(B.web_plate.thickness_provided)) - - flangePlateWeldL = FilletWeld(h=float(B.flange_weld.size), b=float(B.flange_weld.size), L=flangePlate.L) - flangePlateWeldW = FilletWeld(h=float(B.flange_weld.size), b=float(B.flange_weld.size), L=flangePlate.W) - - innerflangePlateWeldL = FilletWeld(h=float(B.flange_weld.size), b=float(B.flange_weld.size), - L=innerFlangePlate.L) - innerflangePlateWeldW = FilletWeld(h=float(B.flange_weld.size), b=float(B.flange_weld.size), - L=innerFlangePlate.W) - - webPlateWeldL = FilletWeld(h=float(B.web_weld.size), b=float(B.web_weld.size), L=webPlate.L) - webPlateWeldW = FilletWeld(h=float(B.web_weld.size), b=float(B.web_weld.size), L=webPlate.W) - - bbCoverPlate = BBSpliceCoverPlateWeldedCAD(B, beam, flangePlate, innerFlangePlate, webPlate, - flangePlateWeldL, flangePlateWeldW, - innerflangePlateWeldL, - innerflangePlateWeldW, webPlateWeldL, webPlateWeldW) - - # bbCoverPlate.create_3DModel() will create the CAD model of each component, debugging this line will give moe clarity - bbCoverPlate.create_3DModel() - - return bbCoverPlate - - def createBBEndPlateCAD(self): - """ - Calls the CAD components like beam, plate, stiffeners, fillet and grove weld, nut and bolt. Also calls CAD file - :return: creates CAD model - """ - - BBE = self.module_class - - beam_tw = float(BBE.beam_tw) - beam_T = float(BBE.beam_tf) - beam_d = float(BBE.beam_D) - beam_B = float(BBE.beam_bf) - beam_R1 = 0.0 - beam_R2 = 0.0 - beam_alpha = 0.0 - beam_length = 500 - - - - beam_Left = ISection(B=beam_B, T=beam_T, D=beam_d, t=beam_tw, - R1=beam_R1, R2=beam_R2, alpha=beam_alpha, - length=beam_length, notchObj=None) - beam_Right = copy.copy(beam_Left) # Since both the beams are same - - - plate_Left = Plate(W=BBE.ep_width_provided, - L=BBE.ep_height_provided, - T=BBE.plate_thickness) - plate_Right = copy.copy(plate_Left) # Since both the end plates are identical - - # Beam stiffeners 4 if extended both ways, only 1 and 3 if extended oneway and non for flus type - beam_stiffeners = StiffenerPlate(W=BBE.stiffener_height, L=BBE.stiffener_length, - T=BBE.stiffener_thickness, - R11=BBE.stiffener_length - 25, - R12=BBE.stiffener_height - 25, - L21=5.0, L22=5.0) # TODO: given hard inputs to L21 and L22 - # - # # Beam stiffeners for the flush type endplate - beam_stiffenerFlush = StiffenerPlate(W=BBE.stiffener_height, L=BBE.stiffener_length, - T=BBE.stiffener_thickness, - L21=5.0, L22=5.0) - - - # alist = self.designParameters() # An object to save all input values entered by user - - bolt_d = float(BBE.bolt_diameter_provided) # Bolt diameter, entered by user - bolt_r = bolt_d / 2 - print(bolt_d) - bolt_T = self.boltHeadThick_Calculation(bolt_d) - bolt_R = self.boltHeadDia_Calculation(bolt_d) / 2 - bolt_Ht = self.boltLength_Calculation(bolt_d) - - bolt = Bolt(R=bolt_R, T=bolt_T, H=bolt_Ht, r=bolt_r) # Call to create Bolt from Component repo - nut_T = self.nutThick_Calculation(bolt_d) - nut_Ht = nut_T - nut = Nut(R=bolt_R, T=nut_T, H=nut_Ht, innerR1=bolt_r) - - numberOfBolts = int(BBE.bolt_numbers) - - nutSpace = 2 * float(BBE.plate_thickness) + nut_T # Space between bolt head and nut - - bbNutBoltArray = BBENutBoltArray(BBE, nut, bolt, numberOfBolts, nutSpace) - - # Following welds are for to weld stiffeners for extended bothways and ext4ended oneway - # bbWeld for stiffener hight on left side - bbWeldStiffHeight = FilletWeld(b=BBE.weld_size_stiffener, h=BBE.weld_size_stiffener, - - L=BBE.stiffener_height - 5.0) # outputobj['Stiffener']['Length'] - 25 - - # bbWeld for stiffener length on left side - bbWeldStiffLength = FilletWeld(b=BBE.weld_size_stiffener, h=BBE.weld_size_stiffener, - L=BBE.stiffener_length-5.0) - # - # # following welds are fillet welds for the flush endplate stiffeners - bbWeldFlushstiffHeight = FilletWeld(b=BBE.weld_size_stiffener, h=BBE.weld_size_stiffener, - L=BBE.stiffener_height-5.0) - - bbWeldFlushstiffLength = FilletWeld(b=BBE.weld_size_stiffener, h=BBE.weld_size_stiffener, - L=BBE.stiffener_length-5.0) - # - # # if BBE.weld.type == "Fillet Weld": - # # - # Fillet Weld for connecting end plate to beam - - # # Followings welds are welds above beam flange, Qty = 4 - # bbWeldAbvFlang = FilletWeld(b=float(BBE.flange_weld.size), h=float(BBE.flange_weld.size), - # L=beam_B) - # - # # Followings welds are welds below beam flange, Qty = 8 - # bbWeldBelwFlang = FilletWeld(b=float(BBE.flange_weld.size), h=float(BBE.flange_weld.size), - # L=(beam_B - beam_tw) / 2 - - # beam_R1 - beam_R2) - # - # # Followings welds are welds placed aside of beam web, Qty = 4 - # bbWeldSideWeb = FilletWeld(b=float(BBE.web_weld.size), h=float(BBE.web_weld.size), - # L=beam_d - 2 * (beam_T + beam_R1) - (2 * 5)) - # # # - # # extbothWays = BBEndplateCAD(beam_Left, beam_Right, plate_Left, plate_Right, bbNutBoltArray, - # # bbWeldAbvFlang, bbWeldBelwFlang, bbWeldSideWeb, bbWeldFlushstiffHeight, - # # bbWeldFlushstiffLength, - # # bbWeldStiffHeight, bbWeldStiffLength, beam_stiffeners, beam_stiffenerFlush, alist, - # # outputobj) - # # extbothWays.create_3DModel() - # # - # # return extbothWays - # # - # # else: # Groove Weld - # - # # Grove Weld for connecting end plate to beam - bbWeldFlang = GrooveWeld(b=float(beam_tw), h=float(beam_T), - L=beam_B) # outputobj["Weld"]["Size"] - # - # # Followings welds are welds placed aside of beam web, Qty = 4 # edited length value by Anand Swaroop - # bbWeldSideWeb = FilletWeld(b=float(BBE.web_weld.size), h=float(BBE.web_weld.size), - # L=beam_d - 2 * (beam_T + beam_R1) - (2 * 5)) - bbWeldWeb = GrooveWeld(b=float(beam_tw), h=float(beam_tw), - L=beam_d - 2 * beam_T) # outputobj["Weld"]["Size"] - - - - extbothWays = CADGroove(BBE,beam_Left, beam_Right, plate_Left, plate_Right, bbNutBoltArray,bbWeldFlang, - bbWeldWeb,beam_stiffeners,beam_stiffenerFlush,bbWeldStiffHeight,bbWeldStiffLength,bbWeldFlushstiffHeight,bbWeldFlushstiffLength) - extbothWays.create_3DModel() - - return extbothWays - - def createBCEndPlateCAD(self): - """ - Calls the CAD components like beam, plate, stiffeners, fillet and grove weld, nut and bolt. Also calls CAD file - :return: creates CAD model - """ - BCE = self.module_class - - column_tw = float(BCE.column_tw) - column_T = float(BCE.column_tf) - column_d = float(BCE.column_D) - column_B = float(BCE.column_bf) - column_R1 = float(BCE.column_r1) - column_R2 = float(BCE.column_r2) - column_alpha = 0.0 - self.column_length = float(BCE.ep_height_provided + 1000) - # print(column_T,column_B,column_d,column_tw,column_R1,column_R2) - - beam_tw = float(BCE.beam_tw) - beam_T = float(BCE.beam_tf) - beam_d = float(BCE.beam_D) - beam_B = float(BCE.beam_bf) - beam_R1 = float(BCE.beam_r1) - beam_R2 = float(BCE.beam_r2) - beam_alpha = 0.0 - self.beam_length = BCE.stiffener_length +500 - - beam_Left = ISection(B=column_B, T=column_T, D=column_d, t=column_tw, - R1=column_R1, R2=column_R2, alpha=column_alpha, - length=self.column_length, notchObj=None) - - beam_Right = ISection(B=beam_B, T=beam_T, D=beam_d, t=beam_tw, - R1=beam_R1, R2=beam_R2, alpha=beam_alpha, - length=self.beam_length, notchObj=None) # Since both the beams are same - - # outputobj = self.outputs # Save all the claculated/displayed out in outputobj - - plate_Right = Plate(W=BCE.ep_width_provided, - L=BCE.ep_height_provided, - T=BCE.plate_thickness) - - - - # TODO adding enpplate type and check if code is working - # TODO added connectivity type here - - - - if BCE.connectivity == "Column Web-Beam Web": - conn_type = 'col_web_connectivity' - else: # "Column flange-Beam web" - conn_type = 'col_flange_connectivity' - - print(conn_type,"hfhfh") - - # endplate_type = alist['Member']['EndPlate_type'] - if BCE.endplate_type == 'Extended One Way - Irreversible Moment': - endplate_type = "one_way" - elif BCE.endplate_type == 'Flushed - Reversible Moment': - endplate_type = "flush" - else: # uiObj['Member']['EndPlate_type'] == "Extended both ways": - endplate_type = "both_way" - - if BCE.continuity_plate_tension_flange_status == True or BCE.continuity_plate_tension_flange_status == True: - - if BCE.connectivity != "Column Web-Beam Web": - contPlates = StiffenerPlate(W=(float(column_B) - float(column_tw)) / 2, - L=float(column_d) - 2 * float(column_T), - T=BCE.cont_plate_thk_provided, L21=BCE.notch_size, R22=BCE.notch_size, - R21=BCE.notch_size, L22=BCE.notch_size) - - contWeldD = FilletWeld(b=BCE.weld_size_continuity_plate, h=BCE.weld_size_continuity_plate, - L=float(column_d) - 2 * float(column_T)-2*BCE.notch_size) - contWeldB = FilletWeld(b=BCE.weld_size_continuity_plate, h=BCE.weld_size_continuity_plate, - L=float(column_B) / 2 - float(column_tw) / 2-BCE.notch_size) - else: - contPlates = StiffenerPlate(W=(float(column_B) - float(column_tw)) / 2, - L=float(column_d) - 2 * float(column_T), - T=BCE.cont_plate_thk_provided, L11=BCE.notch_size, R11=BCE.notch_size, - R12=BCE.notch_size, L12=BCE.notch_size) - contWeldD = FilletWeld(b=BCE.weld_size_continuity_plate, h=BCE.weld_size_continuity_plate, - L=float(column_d) - 2 * float(column_T)-2*BCE.notch_size) - contWeldB = FilletWeld(b=BCE.weld_size_continuity_plate, h=BCE.weld_size_continuity_plate, - L=float(column_B) / 2 - float(column_tw) / 2-BCE.notch_size) - else: - contPlates = None - contWeldD = None - contWeldB = None - - if BCE.web_stiffener_status == True: - - - webplate = StiffenerPlate(W=BCE.web_stiffener_width, - L=BCE.web_stiffener_depth, - T=BCE.web_stiffener_thk_provided) - webWeldD = FilletWeld(b=BCE.weld_size_web_stiffener, h=BCE.weld_size_web_stiffener, - L=BCE.web_stiffener_depth) - webWeldB = FilletWeld(b=BCE.weld_size_web_stiffener, h=BCE.weld_size_web_stiffener, - L=BCE.web_stiffener_width) - else: - webplate = None - webWeldD = None - webWeldB = None - - - - - # if BCE.web_stiffener_status == True: - # diagplate = StiffenerPlate(W=(float(column_B) - float(column_tw)) / 2, - # L=BCE.diag_stiffener_length, - # T=BCE.diag_stiffener_thk_provided) - # diagWeldD = FilletWeld(b=BCE.weld_size_diag_stiffener, h=BCE.weld_size_diag_stiffener, - # L=BCE.diag_stiffener_length) - # diagWeldB = FilletWeld(b=BCE.diag_stiffener_thk_provided, h=BCE.diag_stiffener_thk_provided, - # L=float(column_B) / 2 - float(column_tw) / 2) - # else: - ########## diagplate is omitted due to detailing issues ########### - diagplate = None - diagWeldD = None - diagWeldB = None - ########## diagplate is omitted due to detailing issues ########### - - # contPlate_L2 = StiffenerPlate(W=(float(column_data["B"]) - float(column_data["tw"])) / 2, - # L=float(column_data["D"]) - 2 * float(column_data["T"]), - # T=outputobj['ContPlateTens']['Thickness']) - # contPlate_R1 = copy.copy(contPlate_L1) - # contPlate_R2 = copy.copy(contPlate_L2) - - - - beam_stiffeners = StiffenerPlate(W=BCE.stiffener_height, L=BCE.stiffener_length, - T=BCE.stiffener_thickness, - R11=BCE.stiffener_length- 25, - R12=BCE.stiffener_height - 25, - L21=5.0, L22=5.0) # TODO: given hard inputs to L21 and L22 - - beam_stiffenerFlush = StiffenerPlate(W=BCE.stiffener_height, L=BCE.stiffener_length, - T=BCE.stiffener_thickness, - L21=5.0, L22=5.0) - - bcWeldFlushstiffHeight = FilletWeld(b=BCE.weld_size_stiffener, h=BCE.weld_size_stiffener, - L=BCE.stiffener_height - 5.0) - - bcWeldFlushstiffLength = FilletWeld(b=BCE.weld_size_stiffener, h=BCE.weld_size_stiffener, - L=BCE.stiffener_length - 5.0) - - # beam_stiffener_2 = copy.copy(beam_stiffener_1) - - bolt_d = float(BCE.bolt.bolt_diameter_provided) # Bolt diameter, entered by user - bolt_r = bolt_d / 2 - bolt_T = self.boltHeadThick_Calculation(bolt_d) - bolt_R = self.boltHeadDia_Calculation(bolt_d) / 2 - bolt_Ht = self.boltLength_Calculation(bolt_d) - - bolt = Bolt(R=bolt_R, T=bolt_T, H=bolt_Ht, r=bolt_r) # Call to create Bolt from Component repo - nut_T = self.nutThick_Calculation(bolt_d) - nut_Ht = nut_T - nut = Nut(R=bolt_R, T=nut_T, H=nut_Ht, innerR1=bolt_r) - - numberOfBolts = int(BCE.bolt_numbers) - - - # TODO remove all the clutter later - - # nutSpace = 2 * float(outputobj["Plate"]["Thickness"]) + nut_T # Space between bolt head and nut - if conn_type == 'col_flange_connectivity': - nutSpace = float(column_T) + float(BCE.plate_thickness) + nut_T # / 2 + bolt_T / 2 # Space between bolt head and nut - - else: - nutSpace = float(column_tw) + float(BCE.plate_thickness) + nut_T # / 2 + bolt_T / 2 # Space between bolt head and nut - print(nutSpace,column_tw,BCE.plate_thickness,nut_T,"121") - bbNutBoltArray = BCE_NutBoltArray(BCE, nut, bolt, numberOfBolts, nutSpace, endplate_type) - - ########################### - # WELD SECTIONS # - ########################### - ''' - Following sections are for creating Fillet Welds and Groove Welds - Welds are numbered from Top to Bottom in Z-axis, Front to Back in Y axis and Left to Right in X axis. - ''' - ############################### Weld for the beam stiffeners ################################################ - - # bcWeld for stiffener hight on left side - print(BCE.notch_size,BCE.stiffener_thickness,BCE.stiffener_height,BCE.stiffener_length, BCE.cont_plate_thk_provided,BCE.weld_size_continuity_plate,BCE.weld_size_continuity_plate,"jjjj") - bcWeldStiffHeight = FilletWeld(b=BCE.weld_size_stiffener, h=BCE.weld_size_stiffener, - L=BCE.stiffener_height-5.0) - - # - bcWeldStiffLength = FilletWeld(b=BCE.weld_size_stiffener, h=BCE.weld_size_stiffener, - L=BCE.stiffener_length-5.0) - - - - bcWeldFlang = GrooveWeld(b=float(beam_tw), h=float(beam_T), - L=beam_B) - # # # bcWeldFlang_2 = copy.copy(bcWeldFlang_1) - # # - # # # Followings welds are welds placed aside of beam web, Qty = 4 # edited length value by Anand Swaroop - bcWeldWeb = GrooveWeld(b=float(beam_tw), h=float(beam_tw), - L=beam_d - 2 * beam_T) - - if conn_type == 'col_flange_connectivity': - # - # if alist["Weld"]["Method"] == "Fillet Weld": - # - # # # Followings welds are welds above beam flange, Qty = 4 - # # bcWeldAbvFlang = FilletWeld(b=float(alist["Weld"]["Flange (mm)"]), - # # h=float(alist["Weld"]["Flange (mm)"]), - # # L=beam_B) - # # # bcWeldAbvFlang_22 = copy.copy(bcWeldAbvFlang_21) - # # - # # # Followings welds are welds below beam flange, Qty = 8 - # # bcWeldBelwFlang = FilletWeld(b=float(alist["Weld"]["Flange (mm)"]), - # # h=float(alist["Weld"]["Flange (mm)"]), L=(beam_B - beam_tw) / 2) - # # # bcWeldBelwFlang_22 = copy.copy(bcWeldBelwFlang_21) - # # # bcWeldBelwFlang_23 = copy.copy(bcWeldBelwFlang_21) - # # # bcWeldBelwFlang_24 = copy.copy(bcWeldBelwFlang_21) - # # - # # # Followings welds are welds placed aside of beam web, Qty = 4 # edited length value by Anand Swaroop - # # bcWeldSideWeb = FilletWeld(b=float(alist["Weld"]["Web (mm)"]), h=float(alist["Weld"]["Web (mm)"]), - # # L=beam_d - 2 * beam_T - 40) - # # # bcWeldSideWeb_22 = copy.copy(bcWeldSideWeb_21) - # - # extbothWays = CADFillet(beam_Left, beam_Right, plate_Right, bbNutBoltArray, bolt, bcWeldAbvFlang, - # bcWeldBelwFlang, - # bcWeldSideWeb, contWeldD, contWeldB, - # bcWeldStiffHeight, bcWeldStiffLength, - # contPlates, beam_stiffeners, endplate_type, conn_type, - # outputobj) - # extbothWays.create_3DModel() - # - # return extbothWays - # - # else: # Groove Weld - - # extbothWays = CADGroove(beam_Left, beam_Right, plate_Right, bbNutBoltArray, bolt, - # bcWeldFlang, bcWeldWeb, - # bcWeldStiffHeight, bcWeldStiffLength, contWeldD, contWeldB, - # contPlates, beam_stiffeners, endplate_type, outputobj) - extbothWays = BCECADGroove(BCE,beam_Left, beam_Right, plate_Right, bbNutBoltArray, bolt,bcWeldFlang, - bcWeldWeb, contPlates,beam_stiffeners,bcWeldStiffHeight,bcWeldStiffLength,contWeldD,contWeldB,diagplate, diagWeldD, diagWeldB, webplate, webWeldB, webWeldD, beam_stiffenerFlush,bcWeldFlushstiffHeight, bcWeldFlushstiffLength,endplate_type) - - - extbothWays.create_3DModel() - - return extbothWays - - else: # conn_type = 'col_web_connectivity' - bcWeldFlang = GrooveWeld(b=float(beam_tw), h=float(beam_T), - L=beam_B) - # # # bcWeldFlang_2 = copy.copy(bcWeldFlang_1) - # # - # # # Followings welds are welds placed aside of beam web, Qty = 4 # edited length value by Anand Swaroop - bcWeldWeb = GrooveWeld(b=float(beam_tw), h=float(beam_tw), - L=beam_d - 2 * beam_T) - - ########## diagplate is omitted due to detailing issues ########### - diagplate = None - diagWeldD = None - diagWeldB = None - ########## diagplate is omitted due to detailing issues ########### - - webplate = None - webWeldD = None - webWeldB = None - # if alist["Weld"]["Method"] == "Fillet Weld": - # # # Followings welds are welds above beam flange, Qty = 4 - # # bcWeldAbvFlang_21 = FilletWeld(b=float(alist["Weld"]["Flange (mm)"]), - # # h=float(alist["Weld"]["Flange (mm)"]), - # # L=beam_B) - # # bcWeldAbvFlang_22 = copy.copy(bcWeldAbvFlang_21) - # # - # # # Followings welds are welds below beam flange, Qty = 8 - # # bcWeldBelwFlang_21 = FilletWeld(b=float(alist["Weld"]["Flange (mm)"]), - # # h=float(alist["Weld"]["Flange (mm)"]), L=(beam_B - beam_tw) / 2) - # # bcWeldBelwFlang_22 = copy.copy(bcWeldBelwFlang_21) - # # bcWeldBelwFlang_23 = copy.copy(bcWeldBelwFlang_21) - # # bcWeldBelwFlang_24 = copy.copy(bcWeldBelwFlang_21) - # # - # # # Followings welds are welds placed aside of beam web, Qty = 4 # edited length value by Anand Swaroop - # # bcWeldSideWeb_21 = FilletWeld(b=float(alist["Weld"]["Web (mm)"]), h=float(alist["Weld"]["Web (mm)"]), - # # L=beam_d - 2 * beam_T - 40) - # # bcWeldSideWeb_22 = copy.copy(bcWeldSideWeb_21) - - # col_web_connectivity = CADColWebFillet(beam_Left, beam_Right, plate_Right, bbNutBoltArray, bolt, - # bcWeldAbvFlang, - # bcWeldBelwFlang, - # bcWeldSideWeb, - # contWeldD, contWeldB, - # bcWeldStiffHeight, bcWeldStiffLength, - # contPlates, beam_stiffeners, endplate_type, - # conn_type, outputobj) - # - # col_web_connectivity.create_3DModel() - # - # return col_web_connectivity - # - # else: # Groove Weld - - # else: - - ####################################### - # WELD SECTIONS QUARTER CONE # - ####################################### - - # col_web_connectivity = CADcolwebGroove(beam_Left, beam_Right, plate_Right, bbNutBoltArray, bolt, - # bcWeldFlang, bcWeldWeb, - # bcWeldStiffHeight, bcWeldStiffLength, - # contWeldD, contWeldB, - # contPlates, beam_stiffeners, endplate_type, - # outputobj) - - col_web_connectivity = CADcolwebGroove(BCE, beam_Left, beam_Right, plate_Right, bbNutBoltArray, bolt, - bcWeldFlang,bcWeldWeb,contPlates,beam_stiffeners,bcWeldStiffHeight,bcWeldStiffLength,contWeldD,contWeldB, diagplate, diagWeldD, diagWeldB, webplate, webWeldB, webWeldD, beam_stiffenerFlush,bcWeldFlushstiffHeight, bcWeldFlushstiffLength,endplate_type) - - - col_web_connectivity.create_3DModel() - - return col_web_connectivity - - - - def createCCCoverPlateCAD(self): - - if self.connection == KEY_DISP_COLUMNCOVERPLATE: - C = self.module_class() - columnLenght = (max(float(C.flange_plate.length), float(C.web_plate.length)) + 600) / 2 - # column = ISection(B=206.4, T=17.3, D=215.8, t=10, R1=15, R2=75, alpha=94, length=1000, notchObj=None) - # flangePlate = Plate(L=240, W=203.6, T=10) - # innerFlangePlate = Plate(L=240, W=85, T=10) - # webPlate = Plate(L=600, W=120, T=8) - # gap = 10 - column = ISection(B=float(C.section.flange_width), T=float(C.section.flange_thickness), - D=float(C.section.depth), t=float(C.section.web_thickness), - R1=float(C.section.root_radius), - R2=float(C.section.toe_radius), alpha=float(C.section.flange_slope), length=columnLenght, - notchObj=None) - flangePlate = Plate(L=float(C.flange_plate.length), W=float(C.flange_plate.height), - T=float(C.flange_plate.thickness_provided)) - innerFlangePlate = Plate(L=float(C.flange_plate.Innerlength), W=float(C.flange_plate.Innerheight), - T=float(C.flange_plate.thickness_provided)) - webPlate = Plate(L=float(C.web_plate.length), W=float(C.web_plate.height), - T=float(C.web_plate.thickness_provided)) - - bolt_d = float(C.bolt.bolt_diameter_provided) # Bolt diameter (shank part), entered by user - bolt_r = bolt_d / 2 # Bolt radius (Shank part) - bolt_T = self.boltHeadThick_Calculation(bolt_d) # Bolt head thickness - bolt_R = self.boltHeadDia_Calculation(bolt_d) / 2 # Bolt head diameter (Hexagon) - bolt_Ht = self.boltLength_Calculation(bolt_d) # Bolt head height - - bolt = Bolt(R=bolt_R, T=bolt_T, H=bolt_Ht, r=bolt_r) # Call to create Bolt from Component directory - nut_T = self.nutThick_Calculation(bolt_d) # Nut thickness, usually nut thickness = nut height - nut_Ht = nut_T - nut = Nut(R=bolt_R, T=nut_T, H=nut_Ht, innerR1=bolt_r) - if C.preference != 'Outside': - nut_space = 2 * flangePlate.T + column.T - nut_spaceW = 2 * webPlate.T + column.t - else: - nut_space = flangePlate.T + column.T - nut_spaceW = 2*webPlate.T + column.t - - numOfboltsF = C.flange_plate.bolts_required - numOfboltsW = C.web_plate.bolts_required - - nut_bolt_array_AF = CCSpliceNutBolt_AF(C, nut, bolt, numOfboltsF, nut_space) - nut_bolt_array_BF = CCSpliceNutBolt_BF(C, nut, bolt, numOfboltsF, nut_space) - nut_bolt_array_Web = CCSpliceNutBolt_Web(C, nut, bolt, numOfboltsW, nut_spaceW) - - ccCoverPlateCAD = CCSpliceCoverPlateBoltedCAD(C, column, flangePlate, innerFlangePlate, webPlate, - nut_bolt_array_AF, nut_bolt_array_BF, - nut_bolt_array_Web) - - ccCoverPlateCAD.create_3DModel() - - - elif self.connection == KEY_DISP_COLUMNCOVERPLATEWELD: - - C = self.module_class() - columnLenght = (max(float(C.flange_plate.length), float(C.web_plate.length)) + 600) / 2 - column = ISection(B=float(C.section.flange_width), T=float(C.section.flange_thickness), - D=float(C.section.depth), t=float(C.section.web_thickness), - R1=float(C.section.root_radius), - R2=float(C.section.toe_radius), alpha=float(C.section.flange_slope), length=columnLenght, - notchObj=None) - flangePlate = Plate(L=float(C.flange_plate.length), W=float(C.flange_plate.height), - T=float(C.flange_plate.thickness_provided)) - innerFlangePlate = Plate(L=float(C.flange_plate.Innerlength), W=float(C.flange_plate.Innerheight), - T=float(C.flange_plate.thickness_provided)) - webPlate = Plate(L=float(C.web_plate.length), W=float(C.web_plate.height), - T=float(C.web_plate.thickness_provided)) - - flangePlateWeldL = FilletWeld(h=float(C.flange_weld.size), b=float(C.flange_weld.size), L=flangePlate.L) - flangePlateWeldW = FilletWeld(h=float(C.flange_weld.size), b=float(C.flange_weld.size), L=flangePlate.W) - - innerflangePlateWeldL = FilletWeld(h=float(C.flange_weld.size), b=float(C.flange_weld.size), - L=innerFlangePlate.L) - innerflangePlateWeldW = FilletWeld(h=float(C.flange_weld.size), b=float(C.flange_weld.size), - L=innerFlangePlate.W) - - webPlateWeldL = FilletWeld(h=float(C.web_weld.size), b=float(C.web_weld.size), L=webPlate.L) - webPlateWeldW = FilletWeld(h=float(C.web_weld.size), b=float(C.web_weld.size), L=webPlate.W) - - ccCoverPlateCAD = CCSpliceCoverPlateWeldedCAD(C, column, flangePlate, innerFlangePlate, webPlate, - flangePlateWeldL, flangePlateWeldW, - innerflangePlateWeldL, - innerflangePlateWeldW, webPlateWeldL, webPlateWeldW) - - ccCoverPlateCAD.create_3DModel() - - return ccCoverPlateCAD - - def createCCEndPlateCAD(self): - CEP = self.module_class - - bolt_d = float(CEP.bolt_diam_provided) # Bolt diameter (shank part), entered by user - bolt_r = bolt_d / 2 # Bolt radius (Shank part) - bolt_T = self.boltHeadThick_Calculation(bolt_d) # Bolt head thickness - bolt_R = self.boltHeadDia_Calculation(bolt_d) / 2 # Bolt head diameter (Hexagon) - bolt_Ht = self.boltLength_Calculation(bolt_d) # Bolt head height - - bolt = Bolt(R=bolt_R, T=bolt_T, H=bolt_Ht, r=bolt_r) # Call to create Bolt from Component directory - nut_T = self.nutThick_Calculation(bolt_d) # Nut thickness, usually nut thickness = nut height - nut_Ht = nut_T - nut = Nut(R=bolt_R, T=nut_T, H=nut_Ht, innerR1=bolt_r) - if CEP.weld_size <= 16: - stiffener = StiffenerPlate(L=CEP.stiff_wt, W=CEP.stiff_ht, T=CEP.t_s, L11=CEP.stiff_wt / 2, - L12=CEP.stiff_ht / 2, R21=10, R22=10) - weld_stiff_h = GrooveWeld(b=stiffener.T, h= stiffener.T, L=stiffener.L - stiffener.R22) - weld_stiff_v = FilletWeld(b= CEP.weld_size, h= CEP.weld_size, L=stiffener.W - stiffener.R21) - else: - stiffener = StiffenerPlate(L=CEP.stiff_wt - CEP.t_s, W=CEP.stiff_ht, T=CEP.t_s, L11=CEP.stiff_wt / 2, - L12=CEP.stiff_ht / 2, R21=10, R22=10) - weld_stiff_h = GrooveWeld(b=stiffener.T, h= stiffener.T, L=stiffener.L - stiffener.R22) - weld_stiff_v = GrooveWeld(b=stiffener.T, h= stiffener.T, L=stiffener.W - stiffener.R21) - - column = ISection(B=float(CEP.section.flange_width), T=float(CEP.section.flange_thickness), - D=float(CEP.section.depth), t=float(CEP.section.web_thickness), - R1=float(CEP.section.root_radius), R2=float(CEP.section.toe_radius), - alpha=float(CEP.section.flange_slope), length=1000, notchObj=None) - endPlate = Plate(L=float(CEP.plate_height), W=float(CEP.plate_width), T=float(CEP.plate_thickness_provided)) - flangeWeld = GrooveWeld(b=column.T, h=float(10.0), L=column.B) - webWeld = GrooveWeld(b=column.t, h=flangeWeld.h, L=column.D - 2 * column.T) - - # bolt = Bolt(R=14, T=10, H=13, r=8) - # nut = Nut(R=bolt.R, T=bolt.T, H=bolt.T + 1, innerR1=bolt.r) - nut_space = 2 * endPlate.T + nut.T # member.T + plate.T + nut.T - - nut_bolt_array = CEPNutBoltArray(CEP, column, nut, bolt, nut_space) - - ccEndPlateCad = CCEndPlateCAD(CEP, column, endPlate, flangeWeld, webWeld, nut_bolt_array, stiffener, weld_stiff_h, weld_stiff_v) - - ccEndPlateCad.create_3DModel() - - return ccEndPlateCad - - def createBasePlateCAD(self): - """ - :return: The calculated values/parameters to create 3D CAD model of individual components. - """ - - BP = self.module_class - - if BP.connectivity == 'Hollow/Tubular Column Base': - if BP.dp_column_designation[1:4] == 'SHS' or BP.dp_column_designation[1:4] == 'RHS': - sec = RectHollow(L=float(BP.column_bf), W=float(BP.column_D), H=1000, T=float(BP.column_tf)) - - BP.weld_size_stiffener = max(sec.T, BP.stiffener_plt_thk)/2 - weld_sec = RectHollow(L=sec.L, W=sec.W, H=float(BP.weld_size_stiffener), T=sec.T) - stiff_alg_l = StiffenerPlate(L=BP.stiffener_plt_len_along_D - BP.weld_size_stiffener, W=BP.stiffener_plt_height, T= BP.stiffener_plt_thk, - L11= BP.stiffener_plt_len_along_D - BP.weld_size_stiffener - 50, L12=BP.stiffener_plt_height - 100, R21=15, R22=15) - stiff_alg_b = StiffenerPlate(L= BP.stiffener_plt_len_along_B - BP.weld_size_stiffener, W=BP.stiffener_plt_height, T=BP.stiffener_plt_thk, - L11= BP.stiffener_plt_len_along_B - BP.weld_size_stiffener - 50, L12=BP.stiffener_plt_height - 100, R21=15, R22=15) - - weld_stiff_l_v = GrooveWeld(b=stiff_alg_l.T, h=BP.weld_size_stiffener, L=stiff_alg_l.W - stiff_alg_l.R22) - weld_stiff_l_h = GrooveWeld(b=stiff_alg_l.T, h=BP.weld_size_stiffener, L=stiff_alg_l.L - stiff_alg_l.R22) - weld_stiff_b_v = GrooveWeld(b=stiff_alg_b.T, h=BP.weld_size_stiffener, L=stiff_alg_b.W - stiff_alg_b.R22) - weld_stiff_b_h = GrooveWeld(b=stiff_alg_b.T, h=BP.weld_size_stiffener, L=stiff_alg_b.L - stiff_alg_b.R22) - - - else: #self.BP.dp_column_designation[1:4] == 'CHS': - sec = CircularHollow(r=float(BP.column_D)/ 2, T=float(BP.column_tf), H=1500) - - BP.weld_size_stiffener = max(sec.T, BP.stiffener_plt_thk)/2 - - weld_sec = CircularHollow(r=sec.r, T=sec.T, H=float(BP.weld_size_stiffener)) - stiff_alg_l = StiffenerPlate(L=BP.stiffener_plt_len_across_D - BP.weld_size_stiffener, W=BP.stiffener_plt_height, T=BP.stiffener_plt_thk, - L11=BP.stiffener_plt_len_across_D - BP.weld_size_stiffener - 50, L12=BP.stiffener_plt_height - 100, R21=15, R22=15) - stiff_alg_b = StiffenerPlate(L=BP.stiffener_plt_len_across_D - BP.weld_size_stiffener, W=BP.stiffener_plt_height, T=BP.stiffener_plt_thk, - L11=BP.stiffener_plt_len_across_D - BP.weld_size_stiffener - 50, L12=BP.stiffener_plt_height - 100, R21=15, R22=15) - - weld_stiff_l_v = GrooveWeld(b=stiff_alg_l.T, h=BP.weld_size_stiffener, L=stiff_alg_l.W - stiff_alg_l.R22) - weld_stiff_l_h = GrooveWeld(b=stiff_alg_l.T, h=BP.weld_size_stiffener, L=stiff_alg_l.L - stiff_alg_l.R22) - weld_stiff_b_v = GrooveWeld(b=stiff_alg_b.T, h=BP.weld_size_stiffener, L=stiff_alg_b.W - stiff_alg_b.R22) - weld_stiff_b_h = GrooveWeld(b=stiff_alg_b.T, h=BP.weld_size_stiffener, L=stiff_alg_b.L - stiff_alg_b.R22) - - baseplate = Plate(L=float(BP.bp_length_provided), W=float(BP.bp_width_provided), T=float(BP.plate_thk)) - - bolt_d = float(BP.anchor_dia_outside_flange) - bolt_r = bolt_d / 2 # Bolt radius (Shank part) - bolt_R = self.boltHeadDia_Calculation(bolt_d) / 2 # Bolt head diameter (Hexagon) - # bolt_T = self.boltHeadThick_Calculation(bolt_d) # Bolt head thickness - nut_T = self.nutThick_Calculation(bolt_d) # Nut thickness, usually nut thickness = nut height - nut_HT = nut_T - - ex_length_out = BP.anchor_len_above_footing_out - if BP.anchor_type == 'IS 5624-Type A': - bolt = AnchorBolt_A(l=float(BP.anchor_len_below_footing_out), c=125, a=75, - r=float(BP.anchor_dia_outside_flange) / 2, - ex=ex_length_out) - elif BP.anchor_type == 'IS 5624-Type B': - bolt = AnchorBolt_B(l=float(BP.anchor_len_below_footing_out), r=float(BP.anchor_dia_outside_flange) / 2, - ex=ex_length_out) - else: # BP.anchor_type == 'End Plate Type': - bolt = AnchorBolt_Endplate(l=float(BP.anchor_len_below_footing_out), r=float(BP.anchor_dia_outside_flange) / 2, a= BP.plate_washer_dim_out*1.5, - ex=ex_length_out) - - bolt_in = bolt - - nut = Nut(R=bolt_R, T=nut_T, H=nut_HT, innerR1=bolt_r) - nut_in = nut - washer = Washer(a=BP.plate_washer_dim_out , d=BP.plate_washer_inner_dia_out , t=BP.plate_washer_thk_out) - washer_in = washer - nutSpace = bolt.c + baseplate.T - bolthight = washer.T + nut.T + 50 - - concrete = Plate(L=baseplate.L * 1.5, W=baseplate.W * 1.5, T=bolt.l * 1.2) - grout = Grout(L=baseplate.L * 1.5, W=baseplate.W * 1.5, T=50) - - if BP.shear_key_along_ColDepth == 'Yes': - shearkey_1 = Plate(L=float(BP.shear_key_len_ColDepth), W=float(BP.shear_key_thk), T=float(BP.shear_key_depth_ColDepth)) - else: - shearkey_1 = Plate(L=float(0), W=float(0), T=float(0)) - - if BP.shear_key_along_ColWidth == 'Yes': - shearkey_2 = Plate(L=float(BP.shear_key_thk), W=float(BP.shear_key_len_ColWidth), T=float(BP.shear_key_depth_ColWidth)) - else: - shearkey_2 = Plate(L=float(0), W=float(0), T=float(0)) - - nut_bolt_array = bpNutBoltArray(BP, nut, nut_in, bolt, bolt_in, nutSpace, washer, washer_in) - - basePlate = HollowBasePlateCad(BP, sec, weld_sec, nut_bolt_array, bolthight, baseplate, concrete, grout, - stiff_alg_l, stiff_alg_b, weld_stiff_l_v, weld_stiff_l_h, weld_stiff_b_v, - weld_stiff_b_h, shearkey_1, shearkey_2) - else: - column_tw = float(BP.column_tw) - column_T = float(BP.column_tf) - column_d = float(BP.column_D) - column_B = float(BP.column_bf) - column_R1 = float(BP.column_r1) - column_R2 = float(BP.column_r2) - column_alpha = 94 # Todo: connect this. Waiting for danish to give variable - column_length = 1500 - - column = ISection(B=column_B, T=column_T, D=column_d, t=column_tw, R1=column_R1, R2=column_R2, - alpha=column_alpha, length=column_length, notchObj=None) - baseplate = Plate(L=float(BP.bp_length_provided), W=float(BP.bp_width_provided), T=float(BP.plate_thk)) - - if BP.weld_type == 'Fillet Weld': - weldAbvFlang = FilletWeld(b=float(BP.weld_size_flange), h=float(BP.weld_size_flange), L=column.B) - weldBelwFlang = FilletWeld(b=float(BP.weld_size_flange), h=float(BP.weld_size_flange), - L=(column.B - column.t - 2 * (column.R1 + column.R2)) / 2) - weldSideWeb = FilletWeld(b=float(BP.weld_size_web), h=float(BP.weld_size_web), - L=column.D - 2 * (column.t + column.R1)) - else: - BP.weld_size_flange = max(column.T/2, column.t/2) - BP.weld_size_web = BP.weld_size_flange - weldAbvFlang = GrooveWeld(b= column.T, h=float(BP.weld_size_flange), L=column.B) - weldBelwFlang = GrooveWeld(b= column.T, h=float(BP.weld_size_flange), L=column.B) - weldSideWeb = GrooveWeld(b=column.t, h=float(BP.weld_size_web), L=column.D) - - - BP.weld_size_stiffener = max(BP.stiffener_plt_thick_along_web, BP.stiffener_plt_thick_across_web, column.T) / 2 - stiffener = StiffenerPlate(L=float(BP.stiffener_plt_len_along_web) - float(BP.weld_size_stiffener), W=float(BP.stiffener_plt_height_along_web), - T=float(BP.stiffener_plt_thick_along_web), - L11=float(BP.stiffener_plt_len_along_web - 50), L12=float(BP.stiffener_plt_height_along_web - 100), R21=15, R22=15) - - concrete = Plate(L=baseplate.L * 2, W=baseplate.W * 2, T=float(BP.anchor_len_below_footing_out) * 1.5) - grout = Grout(L=concrete.L, W=concrete.W, T=50) - - stiffener_acrsWeb = StiffenerPlate(L=float(BP.stiffener_plt_len_across_web) - float(BP.weld_size_stiffener), W=float(BP.stiffener_plt_height_across_web), T=float(BP.stiffener_plt_thick_across_web), - L11=float(BP.stiffener_plt_len_across_web) - 50, L12=float(BP.stiffener_plt_height_across_web) - 100, - R21=15, R22=15) # todo: add L21 and L22 as max(15, weldsize + 3) - - stiffener_algflangeL = Stiffener_flange(H=float(BP.stiffener_plt_height_along_flange), L=BP.stiffener_plt_len_along_flange - float(BP.weld_size_stiffener), T=BP.stiffener_plt_thick_along_flange, - t_f=column.T, L_h=50, L_v=100, to_left=True) - stiffener_algflangeR = Stiffener_flange(H=float(BP.stiffener_plt_height_along_flange), L=BP.stiffener_plt_len_along_flange - float(BP.weld_size_stiffener), T= BP.stiffener_plt_thick_along_flange, - t_f=column.T, L_h=50, L_v=100, to_left=False) - stiffener_algflange_tapperLength = (stiffener_algflangeR.T - column.T) * 5 - - stiffener_insideflange = StiffenerPlate(L= (column.D - 2*column.T - 2 * float(BP.weld_size_stiffener)), W= (column.B- column.t - 2*column.R1 - 2 * 5)/2, T =12, R21 = column.R1 + 5, R22= column.R1 + 5, L21 = column.R1 + 5, L22= column.R1 + 5) # self.extraspace=5 - - - weld_stiffener_algflng_v = GrooveWeld(b=column.T, h=float(BP.weld_size_stiffener), L=stiffener_algflangeL.H) - weld_stiffener_algflng_h = FilletWeld(b=float(BP.weld_size_stiffener), h=float(BP.weld_size_stiffener), - L=stiffener_algflangeL.L) # Todo: create another weld for inner side of the stiffener - weld_stiffener_algflag_gh = GrooveWeld(b=stiffener_algflangeR.T, h=float(BP.weld_size_stiffener), - L=stiffener_algflangeL.L - stiffener_algflange_tapperLength) - - weld_stiffener_acrsWeb_v = GrooveWeld(b=stiffener_acrsWeb.T, h=float(BP.weld_size_stiffener), - L=stiffener_acrsWeb.W - stiffener_acrsWeb.R22) - weld_stiffener_acrsWeb_h = FilletWeld(b=10, h=10, L=stiffener_acrsWeb.L - stiffener_acrsWeb.R22) - weld_stiffener_acrsWeb_gh = GrooveWeld(b=stiffener_acrsWeb.T, h=float(BP.weld_size_stiffener), - L=stiffener_acrsWeb.L - stiffener_acrsWeb.R22) - - # gussetweld = GrooveWeld(b=gusset.T, h=float(BP.weld_size_stiffener), L=gusset.L) - weld_stiffener_alongWeb_h = FilletWeld(b=float(BP.weld_size_stiffener), h=float(BP.weld_size_stiffener), L=stiffener.L - stiffener.R22) - weld_stiffener_alongWeb_v = GrooveWeld(b=stiffener.T, h=float(BP.weld_size_stiffener), L=stiffener.W - stiffener.R22) - weld_stiffener_alongWeb_gh = GrooveWeld(b=stiffener.T, h=float(BP.weld_size_stiffener), L=stiffener.L - stiffener.R22) - - weld_stiffener_inflange = GrooveWeld(b=stiffener_insideflange.T, h=float(BP.weld_size_stiffener), L=stiffener_insideflange.W - stiffener_insideflange.R22) - weld_stiffener_inflange_d = GrooveWeld(b=stiffener_insideflange.T, h=float(BP.weld_size_stiffener), - L=stiffener_insideflange.L - stiffener_insideflange.R22 - 2 * weld_stiffener_inflange.h) - - if BP.load_axial_tension > 0: - BP.anchor_len_above_footing_in = BP.anchor_len_above_footing_in - BP.anchor_len_below_footing_in = BP.anchor_len_below_footing_in - BP.anchor_dia_inside_flange = BP.anchor_dia_inside_flange - BP.plate_washer_dim_in = BP.plate_washer_dim_in - BP.plate_washer_inner_dia_in = BP.plate_washer_inner_dia_in - BP.plate_washer_thk_in = BP.plate_washer_thk_in - else: - BP.anchor_len_above_footing_in = BP.anchor_len_above_footing_out - BP.anchor_len_below_footing_in = BP.anchor_len_below_footing_out - BP.anchor_dia_inside_flange = BP.anchor_dia_outside_flange - BP.plate_washer_dim_in = BP.plate_washer_dim_out - BP.plate_washer_inner_dia_in = BP.plate_washer_inner_dia_out - BP.plate_washer_thk_in = BP.plate_washer_thk_out - - bolt_d = float(BP.anchor_dia_outside_flange) - bolt_r = bolt_d / 2 # Bolt radius (Shank part) - bolt_R = self.boltHeadDia_Calculation(bolt_d) / 2 # Bolt head diameter (Hexagon) - # bolt_T = self.boltHeadThick_Calculation(bolt_d) # Bolt head thickness - nut_T = self.nutThick_Calculation(bolt_d) # Nut thickness, usually nut thickness = nut height - nut_HT = nut_T - - bolt_d_in = float(BP.anchor_dia_inside_flange) - bolt_r_in = bolt_d_in / 2 # Bolt radius (Shank part) - bolt_R_in = self.boltHeadDia_Calculation(bolt_d_in) / 2 # Bolt head diameter (Hexagon) - # bolt_T = self.boltHeadThick_Calculation(bolt_d) # Bolt head thickness - nut_T_in = self.nutThick_Calculation(bolt_d_in) # Nut thickness, usually nut thickness = nut height - nut_HT_in = nut_T_in - - ex_length_out = BP.anchor_len_above_footing_out - ex_length_in = BP.anchor_len_above_footing_in - if BP.anchor_type == 'IS 5624-Type A': - bolt = AnchorBolt_A(l=float(BP.anchor_len_below_footing_out), c=125, a=75, r=float(BP.anchor_dia_outside_flange) / 2, - ex=ex_length_out) - bolt_in = AnchorBolt_A(l=float(BP.anchor_len_below_footing_in), c=125, a=75, r=float(BP.anchor_dia_inside_flange) / 2, - ex=ex_length_in) - elif BP.anchor_type == 'IS 5624-Type B': - bolt = AnchorBolt_B(l=float(BP.anchor_len_below_footing_out), r=float(BP.anchor_dia_outside_flange) / 2, ex=ex_length_out) - bolt_in = AnchorBolt_B(l=float(BP.anchor_len_below_footing_in), r=float(BP.anchor_dia_inside_flange) / 2, - ex=ex_length_in) - else: #BP.anchor_type == 'End Plate Type': - bolt = AnchorBolt_Endplate(l=float(BP.anchor_len_below_footing_out), r=float(BP.anchor_dia_outside_flange) / 2, a= BP.plate_washer_dim_out * 1.5, - ex=ex_length_out) - bolt_in = AnchorBolt_Endplate(l=float(BP.anchor_len_below_footing_in), - r=float(BP.anchor_dia_inside_flange) / 2, a= BP.plate_washer_inner_dia_in * 1.5, - ex=ex_length_in) - - nut = Nut(R=bolt_R, T=nut_T, H=nut_HT, innerR1=bolt_r) - nut_in = Nut(R=bolt_R_in, T=nut_T_in, H=nut_HT_in, innerR1=bolt_r_in) - washer = Washer(a=BP.plate_washer_dim_out , d=BP.plate_washer_inner_dia_out , t=BP.plate_washer_thk_out) - washer_in = Washer(a=BP.plate_washer_dim_in, d=BP.plate_washer_inner_dia_in, t=BP.plate_washer_thk_out) - nutSpace = bolt.c + baseplate.T - bolthight = washer.T + nut.T + 50 - - if BP.shear_key_along_ColDepth == 'Yes': - shearkey_1 = Plate(L=float(BP.shear_key_len_ColDepth), W=float(BP.shear_key_thk), T=float(BP.shear_key_depth_ColDepth)) - else: - shearkey_1 = Plate(L=float(0), W=float(0), T=float(0)) - - if BP.shear_key_along_ColWidth == 'Yes': - shearkey_2 = Plate(L=float(BP.shear_key_thk), W=float(BP.shear_key_len_ColWidth), T=float(BP.shear_key_depth_ColWidth)) - else: - shearkey_2 = Plate(L=float(0), W=float(0), T=float(0)) - - nut_bolt_array = bpNutBoltArray(BP, nut, nut_in, bolt, bolt_in, nutSpace, washer, washer_in) - - basePlate = BasePlateCad(BP, column, nut_bolt_array, bolthight, baseplate, shearkey_1, shearkey_2, weldAbvFlang, weldBelwFlang, weldSideWeb, - concrete, stiffener, grout, weld_stiffener_alongWeb_h, weld_stiffener_alongWeb_gh, weld_stiffener_alongWeb_v, - stiffener_algflangeL, stiffener_algflangeR, stiffener_acrsWeb, weld_stiffener_algflng_v, weld_stiffener_algflng_h, weld_stiffener_algflag_gh, - weld_stiffener_acrsWeb_v, weld_stiffener_acrsWeb_h, weld_stiffener_acrsWeb_gh, stiffener_insideflange, weld_stiffener_inflange, weld_stiffener_inflange_d) - - basePlate.create_3DModel() - - return basePlate - - def createTensionCAD(self): - """ - :return: The calculated values/parameters to create 3D CAD model of individual components. - """ - T = self.module_class - - # Types of connections = #'Angles', 'Back to Back Angles', 'Star Angles', 'Channels', 'Back to Back Channels' - if self.connection == KEY_DISP_TENSION_BOLTED: - bolt_d = float(T.bolt.bolt_diameter_provided) # Bolt diameter (shank part), entered by user - bolt_r = bolt_d / 2 # Bolt radius (Shank part) - bolt_T = self.boltHeadThick_Calculation(bolt_d) # Bolt head thickness - bolt_R = self.boltHeadDia_Calculation(bolt_d) / 2 # Bolt head diameter (Hexagon) - bolt_Ht = self.boltLength_Calculation(bolt_d) # Bolt head height - - bolt = Bolt(R=bolt_R, T=bolt_T, H=bolt_Ht, r=bolt_r) # Call to create Bolt from Component directory - nut_T = self.nutThick_Calculation(bolt_d) # Nut thickness, usually nut thickness = nut height - nut_Ht = nut_T - nut = Nut(R=bolt_R, T=nut_T, H=nut_Ht, innerR1=bolt_r) # Call to create Nut from Component directory - - plate = GassetPlate(L=float(T.plate.length + 50), H=float(T.plate.height), - T=float(T.plate.thickness_provided), degree=30) - intermittentPlates = Plate(L=float(T.inter_plate_height), W=float(T.inter_plate_length), T=float(plate.T)) - - - if T.sec_profile == 'Channels' or T.sec_profile == 'Back to Back Channels': - member = Channel(B=float(T.section_size_1.flange_width), T=float(T.section_size_1.flange_thickness), - D=float(T.section_size_1.depth), t=float(T.section_size_1.web_thickness), - R1=float(T.section_size_1.root_radius), R2=float(T.section_size_1.toe_radius), - L=float(T.length)) - if T.sec_profile == 'Channels': - nut_space = member.t + plate.T + nut.T # member.T + plate.T + nut.T - - else: - nut_space = 2 * member.t + plate.T + nut.T # 2*member.T + plate.T + nut.T - - intermittentConnection = IntermittentNutBoltPlateArray(T, nut, bolt, intermittentPlates, nut_space) - nut_bolt_array = TNutBoltArray(T, nut, bolt, nut_space) - tensionCAD = TensionChannelBoltCAD(T, member, plate, nut_bolt_array, intermittentConnection) - - else: - member = Angle(L=float(T.length), A=float(T.section_size_1.max_leg), B=float(T.section_size_1.min_leg), - T=float(T.section_size_1.thickness), R1=float(T.section_size_1.root_radius), - R2=float(T.section_size_1.toe_radius)) - if T.sec_profile == 'Back to Back Angles': - nut_space = 2 * member.T + plate.T + nut.T - else: - nut_space = member.T + plate.T + nut.T - - intermittentConnection = IntermittentNutBoltPlateArray(T, nut, bolt, intermittentPlates, nut_space) - nut_bolt_array = TNutBoltArray(T, nut, bolt, nut_space) - tensionCAD = TensionAngleBoltCAD(T, member, plate, nut_bolt_array, intermittentConnection) - - else: - plate = GassetPlate(L=float(T.plate.length + 50), H=float(T.plate.height), - T=float(T.plate.thickness_provided), degree=30) - - intermittentPlates = Plate(L=float(T.inter_plate_height), W=float(T.inter_plate_length), T=plate.T) - intermittentWelds = FilletWeld(h=float(T.inter_weld_size), b=float(T.inter_weld_size), L=intermittentPlates.W) - weld_plate_array = IntermittentWelds(T, intermittentWelds, intermittentPlates) - - s = max(15, float(T.weld.size)) - plate_intercept = plate.L - s - 50 - if T.sec_profile == 'Channels' or T.sec_profile == 'Back to Back Channels': - member = Channel(B=float(T.section_size_1.flange_width), T=float(T.section_size_1.flange_thickness), - D=float(T.section_size_1.depth), t=float(T.section_size_1.web_thickness), - R1=float(T.section_size_1.root_radius), R2=float(T.section_size_1.toe_radius), - L=float(T.length)) - inline_weld = FilletWeld(b=float(T.weld.size), h=float(T.weld.size), L=float(plate_intercept)) - opline_weld = FilletWeld(b=float(T.weld.size), h=float(T.weld.size), L=float(member.D)) - - - tensionCAD = TensionChannelWeldCAD(T, member, plate, inline_weld, opline_weld, weld_plate_array) - - else: - member = Angle(L=float(T.length), A=float(T.section_size_1.max_leg), B=float(T.section_size_1.min_leg), - T=float(T.section_size_1.thickness), R1=float(T.section_size_1.root_radius), - R2=float(T.section_size_1.toe_radius)) - inline_weld = FilletWeld(b=float(T.weld.size), h=float(T.weld.size), L=float(plate_intercept)) - if T.loc == 'Long Leg': - opline_weld = FilletWeld(b=float(T.weld.size), h=float(T.weld.size), L=float(member.A)) - else: # 'Short Leg' - opline_weld = FilletWeld(b=float(T.weld.size), h=float(T.weld.size), L=float(member.B)) - - # weld_plate_array = IntermittentWelds(T, intermittentWelds, intermittentPlates) - tensionCAD = TensionAngleWeldCAD(T, member, plate, inline_weld, opline_weld, weld_plate_array) - - tensionCAD.create_3DModel() - - return tensionCAD - - def display_3DModel(self, component, bgcolor): - - self.component = component - - self.display.EraseAll() - - self.display.View_Iso() - - self.display.FitAll() - - self.display.DisableAntiAliasing() - - if bgcolor == "gradient_bg": - - self.display.set_bg_gradient_color([51, 51, 102], [150, 150, 170]) - else: - self.display.set_bg_gradient_color([255, 255, 255], [255, 255, 255]) - - if self.mainmodule == "Shear Connection": - - A = self.module_class() - - self.loc = A.connectivity - - - if self.loc == "Column Flange-Beam Web" and self.connection == KEY_DISP_FINPLATE: - # pass - # print("hghghghg") - self.display.View.SetProj(OCC.Core.V3d.V3d_XnegYnegZpos) - elif self.loc == "Column Flange-Beam Web" and self.connection == KEY_DISP_SEATED_ANGLE: - self.display.View.SetProj(OCC.Core.V3d.V3d_XnegYnegZpos) - elif self.loc == "Column Flange-Beam Web" and self.connection == KEY_DISP_SEATED_ANGLE: - self.display.View.SetProj(OCC.Core.V3d.V3d_XposYnegZpos) - - if self.component == "Column": - osdag_display_shape(self.display, self.connectivityObj.get_columnModel(), update=True) - elif self.component == "Beam": - osdag_display_shape(self.display, self.connectivityObj.get_beamModel(), material=Graphic3d_NOT_2D_ALUMINUM, - update=True) - elif component == "cleatAngle": - - osdag_display_shape(self.display, self.connectivityObj.angleModel, color=Quantity_NOC_BLUE1, update=True) - osdag_display_shape(self.display, self.connectivityObj.angleLeftModel, color=Quantity_NOC_BLUE1, - update=True) - nutboltlist = self.connectivityObj.nut_bolt_array.get_models() - for nutbolt in nutboltlist: - osdag_display_shape(self.display, nutbolt, color=Quantity_NOC_SADDLEBROWN, update=True) - - elif component == "SeatAngle": - osdag_display_shape(self.display, self.connectivityObj.topclipangleModel, color=Quantity_NOC_BLUE1, - update=True) - osdag_display_shape(self.display, self.connectivityObj.angleModel, color=Quantity_NOC_BLUE1, update=True) - nutboltlist = self.connectivityObj.nut_bolt_array.get_models() - for nutbolt in nutboltlist: - osdag_display_shape(self.display, nutbolt, color=Quantity_NOC_SADDLEBROWN, update=True) - - elif self.component == "Plate": - osdag_display_shape(self.display, self.connectivityObj.weldModelLeft, color=Quantity_NOC_RED, update=True) - osdag_display_shape(self.display, self.connectivityObj.weldModelRight, color=Quantity_NOC_RED, update=True) - osdag_display_shape(self.display, self.connectivityObj.plateModel, color=Quantity_NOC_BLUE4, update=True) - nutboltlist = self.connectivityObj.nut_bolt_array.get_models() - for nutbolt in nutboltlist: - osdag_display_shape(self.display, nutbolt, color=Quantity_NOC_SADDLEBROWN, update=True) - - elif self.component == "Model": - - osdag_display_shape(self.display, self.connectivityObj.columnModel, update=True) - osdag_display_shape(self.display, self.connectivityObj.beamModel, material=Graphic3d_NOT_2D_ALUMINUM, - update=True) - if self.connection == KEY_DISP_FINPLATE or self.connection == KEY_DISP_ENDPLATE: - osdag_display_shape(self.display, self.connectivityObj.weldModelLeft, color=Quantity_NOC_RED, update=True) - osdag_display_shape(self.display, self.connectivityObj.weldModelRight, color=Quantity_NOC_RED, update=True) - osdag_display_shape(self.display, self.connectivityObj.plateModel, color=Quantity_NOC_BLUE1, - update=True) - - elif self.connection == KEY_DISP_CLEATANGLE: - osdag_display_shape(self.display, self.connectivityObj.angleModel, color=Quantity_NOC_BLUE1, - update=True) - osdag_display_shape(self.display, self.connectivityObj.angleLeftModel, color=Quantity_NOC_BLUE1, - update=True) - else: - osdag_display_shape(self.display, self.connectivityObj.topclipangleModel, color=Quantity_NOC_BLUE1, - update=True) - osdag_display_shape(self.display, self.connectivityObj.angleModel, color=Quantity_NOC_BLUE1, - update=True) - nutboltlist = self.connectivityObj.nut_bolt_array.get_models() - for nutbolt in nutboltlist: - osdag_display_shape(self.display, nutbolt, color=Quantity_NOC_SADDLEBROWN, update=True) - - if self.mainmodule == "Moment Connection": - if self.connection == KEY_DISP_BEAMCOVERPLATE: - - self.B = self.module_class() - # else: - # pass - # - # self.loc = A.connectivity - self.CPObj = self.createBBCoverPlateCAD() # CPBoltedObj is an object which gets all the calculated values of CAD models - if self.component == "Beam": - # Displays both beams - osdag_display_shape(self.display, self.CPObj.get_only_beams_Models(), update=True) - - elif self.component == "Connector": - osdag_display_shape(self.display, self.CPObj.get_flangewebplatesModel(), update=True, - color=Quantity_NOC_BLUE1) - if self.B.preference != 'Outside': - osdag_display_shape(self.display, self.CPObj.get_innetplatesModels(), update=True, - color=Quantity_NOC_BLUE1) - - osdag_display_shape(self.display, self.CPObj.get_nut_bolt_arrayModels(), update=True, - color=Quantity_NOC_YELLOW) - - elif self.component == "Model": - osdag_display_shape(self.display, self.CPObj.get_beamsModel(), update=True) - osdag_display_shape(self.display, self.CPObj.get_flangewebplatesModel(), update=True, - color=Quantity_NOC_BLUE1) - - # Todo: remove velove commented lines - - if self.B.preference != 'Outside': - osdag_display_shape(self.display, self.CPObj.get_innetplatesModels(), update=True, - color=Quantity_NOC_BLUE1) - - osdag_display_shape(self.display, self.CPObj.get_nut_bolt_arrayModels(), update=True, - color=Quantity_NOC_YELLOW) - elif self.connection == KEY_DISP_BB_EP_SPLICE: - self.B = self.module_class() - - self.ExtObj = self.createBBEndPlateCAD() - - if component == "Beam": - osdag_display_shape(self.display, self.ExtObj.get_beam_models(), update=True) - - elif component == "Connector": - osdag_display_shape(self.display, self.ExtObj.get_plate_connector_models(), update=True, - color='Blue') - osdag_display_shape(self.display, self.ExtObj.get_welded_models(), update=True, color='Red') - osdag_display_shape(self.display, self.ExtObj.get_nut_bolt_array_models(), update=True, - color=Quantity_NOC_SADDLEBROWN) - - elif component == "Model": - - # osdag_display_shape(self.display, self.ExtObj.get_models(), update=True) - osdag_display_shape(self.display, self.ExtObj.get_beam_models(), update=True) - osdag_display_shape(self.display, self.ExtObj.get_plate_connector_models(), update=True, - color='Blue') - osdag_display_shape(self.display, self.ExtObj.get_welded_models(), update=True, color='Red') - osdag_display_shape(self.display, self.ExtObj.get_nut_bolt_array_models(), update=True, - color=Quantity_NOC_SADDLEBROWN) - - - - elif self.connection == KEY_DISP_BEAMCOVERPLATEWELD: - self.B = self.module_class() - self.CPObj = self.createBBCoverPlateCAD() - beams = self.CPObj.get_beam_models() - plates = self.CPObj.get_plate_models() - welds = self.CPObj.get_welded_modules() - - if self.component == "Beam": - # Displays both beams - osdag_display_shape(self.display, beams, update=True) - elif self.component == "Connector": - osdag_display_shape(self.display, plates, update=True, color=Quantity_NOC_BLUE1) - osdag_display_shape(self.display, welds, update=True, color=Quantity_NOC_RED) - elif self.component == "Model": - osdag_display_shape(self.display, beams, update=True) - osdag_display_shape(self.display, plates, update=True, color=Quantity_NOC_BLUE1) - osdag_display_shape(self.display, welds, update=True, color=Quantity_NOC_RED) - - elif self.connection == KEY_DISP_COLUMNCOVERPLATE: - self.C = self.module_class() - self.CPObj = self.createCCCoverPlateCAD() - columns = self.CPObj.get_column_models() - plates = self.CPObj.get_plate_models() - nutbolt = self.CPObj.get_nut_bolt_models() - onlycolumn = self.CPObj.get_only_column_models() - - if self.component == "Column": - # Displays both beams - osdag_display_shape(self.display, onlycolumn, update=True) - elif self.component == "Cover Plate": - osdag_display_shape(self.display, plates, update=True, color=Quantity_NOC_BLUE1) - osdag_display_shape(self.display, nutbolt, update=True, color=Quantity_NOC_YELLOW) - elif self.component == "Model": - osdag_display_shape(self.display, columns, update=True) - osdag_display_shape(self.display, plates, update=True, color=Quantity_NOC_BLUE1) - osdag_display_shape(self.display, nutbolt, update=True, color=Quantity_NOC_YELLOW) - - - elif self.connection == KEY_DISP_BCENDPLATE: - self.Bc = self.module_class() - self.ExtObj = self.createBCEndPlateCAD() - - self.display.View.SetProj(OCC.Core.V3d.V3d_XnegYnegZpos) - c_length = self.column_length - # Point1 = gp_Pnt(0.0, 0.0, c_length) - # DisplayMsg(self.display, Point1, self.Bc.supporting_section.designation) - b_length = self.beam_length + self.Bc.supporting_section.depth/2+100 - # Point2 = gp_Pnt(0.0,-b_length, c_length/2) - # DisplayMsg(self.display, Point2, self.Bc.supported_section.designation) - # Displays the beams #TODO ANAND - if component == "Column": - self.display.View_Iso() - osdag_display_shape(self.display, self.ExtObj.columnModel, update=True) - # Point1 = gp_Pnt(-self.Bc.supporting_section.flange_width/2, 0, c_length) - # DisplayMsg(self.display, Point1, self.Bc.supporting_section.designation) - # Point = gp_Pnt(0.0, 0.0, 10) - # DisplayMsg(self.display,Point, "Column") - - elif component == "Beam": - self.display.View_Iso() - osdag_display_shape(self.display, self.ExtObj.beamModel, update=True, - material=Graphic3d_NOT_2D_ALUMINUM) - # Point2 = gp_Pnt(0.0, -b_length, c_length / 2) - # DisplayMsg(self.display, Point2, self.Bc.supported_section.designation) - # , color = 'Dark Gray' - - elif component == "Connector": - osdag_display_shape(self.display, self.ExtObj.get_plate_connector_models(), update=True, - color='Blue') - osdag_display_shape(self.display, self.ExtObj.get_welded_models(), update=True, color='Red') - osdag_display_shape(self.display, self.ExtObj.get_nut_bolt_array_models(), update=True, - color=Quantity_NOC_SADDLEBROWN) - - - elif component == "Model": - - osdag_display_shape(self.display, self.ExtObj.get_column_models(), update=True) - osdag_display_shape(self.display, self.ExtObj.get_beam_models(), update=True, - material=Graphic3d_NOT_2D_ALUMINUM) - osdag_display_shape(self.display, self.ExtObj.get_plate_connector_models(), update=True, - color='Blue') - osdag_display_shape(self.display, self.ExtObj.get_welded_models(), update=True, color='Red') - osdag_display_shape(self.display, self.ExtObj.get_nut_bolt_array_models(), update=True, - color=Quantity_NOC_SADDLEBROWN) - # Point1 = gp_Pnt(self.Bc.supporting_section.flange_width/2, -self.Bc.supporting_section.depth/2, c_length*0.75) - # DisplayMsg(self.display, Point1, self.Bc.supporting_section.designation) - # Point2 = gp_Pnt(self.Bc.supporting_section.flange_width/2, -b_length, c_length / 2) - # DisplayMsg(self.display, Point2, self.Bc.supported_section.designation) - # Erase(DisplayMsg(self.display, Point2, self.Bc.supported_section.designation)) - - - elif self.connection == KEY_DISP_COLUMNCOVERPLATEWELD: - self.C = self.module_class() - self.CPObj = self.createCCCoverPlateCAD() - columns = self.CPObj.get_column_models() - plates = self.CPObj.get_plate_models() - welds = self.CPObj.get_welded_modules() - - if self.component == "Column": - # Displays both beams - osdag_display_shape(self.display, columns, update=True) - elif self.component == "Cover Plate": - osdag_display_shape(self.display, plates, update=True, color=Quantity_NOC_BLUE1) - osdag_display_shape(self.display, welds, update=True, color=Quantity_NOC_RED) - elif self.component == "Model": - osdag_display_shape(self.display, columns, update=True) - osdag_display_shape(self.display, plates, update=True, color=Quantity_NOC_BLUE1) - osdag_display_shape(self.display, welds, update=True, color=Quantity_NOC_RED) - - elif self.connection == KEY_DISP_COLUMNENDPLATE: - self.CEP = self.module_class() - self.CEPObj = self.createCCEndPlateCAD() - columns = self.CEPObj.get_column_models() - plates = self.CEPObj.get_plate_models() - welds = self.CEPObj.get_weld_models() - nutBolts = self.CEPObj.get_nut_bolt_models() - - if self.component == "Column": - osdag_display_shape(self.display, columns, update=True) - - elif self.component == "Connector": - osdag_display_shape(self.display, plates, update=True, color=Quantity_NOC_BLUE1) - osdag_display_shape(self.display, welds, update=True, color=Quantity_NOC_RED) - osdag_display_shape(self.display, nutBolts, update=True, color=Quantity_NOC_YELLOW) - - elif self.component == "Model": - osdag_display_shape(self.display, columns, update=True) - osdag_display_shape(self.display, plates, update=True, color=Quantity_NOC_BLUE1) - osdag_display_shape(self.display, welds, update=True, color=Quantity_NOC_RED) - osdag_display_shape(self.display, nutBolts, update=True, color=Quantity_NOC_YELLOW) - - elif self.connection == KEY_DISP_BASE_PLATE: - self.Bp = self.module_class - - self.BPObj = self.createBasePlateCAD() - - column = self.BPObj.get_column_model() - plate = self.BPObj.get_plate_connector_models() - weld = self.BPObj.get_welded_models() - nut_bolt = self.BPObj.get_nut_bolt_array_models() - conc = self.BPObj.get_concrete_models() - grout = self.BPObj.get_grout_models() - - if self.component == "Model": # Todo: change this into key - osdag_display_shape(self.display, column, update=True) - osdag_display_shape(self.display, plate, color=Quantity_NOC_BLUE1, update=True) - osdag_display_shape(self.display, weld, color=Quantity_NOC_RED, update=True) - osdag_display_shape(self.display, nut_bolt, color=Quantity_NOC_YELLOW, update=True) - osdag_display_shape(self.display, conc, color=GRAY, transparency=0.5, update=True) - osdag_display_shape(self.display, grout, color=GRAY, transparency=0.5, update=True) - - elif self.component == "Column": - osdag_display_shape(self.display, column, update=True) - - elif self.component == "Connector": - osdag_display_shape(self.display, plate, color=Quantity_NOC_BLUE1, update=True) - osdag_display_shape(self.display, weld, color=Quantity_NOC_RED, update=True) - osdag_display_shape(self.display, nut_bolt, color=Quantity_NOC_YELLOW, update=True) - - else: - if self.connection == KEY_DISP_TENSION_BOLTED: - self.T = self.module_class() - self.TObj = self.createTensionCAD() - - member = self.TObj.get_members_models() - plate = self.TObj.get_plates_models() - - nutbolt = self.TObj.get_nut_bolt_array_models() - - onlymember = self.TObj.get_only_members_models() - # distance = self.T.length/2 - (2* self.T.plate.end_dist_provided + (self.T.plate.bolt_line - 1 ) * self.T.plate.pitch_provided) - # Point = gp_Pnt(distance, 0.0, 300) - # DisplayMsg(self.display, Point, self.T.section_size_1.designation) - - - if self.component == "Member": # Todo: change this into key - osdag_display_shape(self.display, onlymember, update=True) - elif self.component == "Plate": - osdag_display_shape(self.display, plate, color=Quantity_NOC_BLUE1, update=True) - osdag_display_shape(self.display, nutbolt, color=Quantity_NOC_YELLOW, update=True) - elif self.component == "Endplate": - endplate = self.TObj.get_end_plates_models() - end_nutbolt = self.TObj.get_end_nut_bolt_array_models() - osdag_display_shape(self.display, endplate, color=Quantity_NOC_BLUE1, update=True) - osdag_display_shape(self.display, end_nutbolt, color=Quantity_NOC_YELLOW, update=True) - else: - connector = BRepAlgoAPI_Fuse(nutbolt, plate).Shape() - shape = BRepAlgoAPI_Fuse(connector, member).Shape() - self.TObj.shape = shape - osdag_display_shape(self.display, member, update=True) - osdag_display_shape(self.display, plate, color=Quantity_NOC_BLUE1, update=True) - osdag_display_shape(self.display, nutbolt, color=Quantity_NOC_YELLOW, update=True) - - - elif self.connection == KEY_DISP_TENSION_WELDED: - self.T = self.module_class() - self.TObj = self.createTensionCAD() - - member = self.TObj.get_members_models() - plate = self.TObj.get_plates_models() - welds = self.TObj.get_welded_models() - if self.component == "Member": # Todo: change this into key - osdag_display_shape(self.display, member, update=True) - elif self.component == "Plate": - osdag_display_shape(self.display, plate, color=Quantity_NOC_BLUE1, update=True) - osdag_display_shape(self.display, welds, color=Quantity_NOC_RED, update=True) - elif self.component == "Endplate": - endplate = self.TObj.get_end_plates_models() - osdag_display_shape(self.display, endplate, color=Quantity_NOC_BLUE1, update=True) - else: - connector = BRepAlgoAPI_Fuse(welds, plate).Shape() - shape = BRepAlgoAPI_Fuse(connector, member).Shape() - self.TObj.shape = shape - osdag_display_shape(self.display, member, update=True) - osdag_display_shape(self.display, plate, color=Quantity_NOC_BLUE1, update=True) - osdag_display_shape(self.display, welds, color=Quantity_NOC_RED, update=True) - # - # def display_msg(self): - # if self.connection == KEY_DISP_TENSION_BOLTED: - # self.T = self.module_class() - # # - # # distance = self.T.length / 2 - ( - # # 2 * self.T.plate.end_dist_provided + (self.T.plate.bolt_line - 1) * self.T.plate.pitch_provided) - # # Point = gp_Pnt(distance, 0.0, 300) - # self.display_msg() - - - - def call_3DModel(self, flag, module_class): # Done - - self.module_class = module_class - - if self.mainmodule == "Shear Connection": - - A = self.module_class() - - self.loc = A.connectivity - - if flag is True: - - if self.loc == CONN_CWBW: - self.connectivityObj = self.create3DColWebBeamWeb() - - elif self.loc == CONN_CFBW: - self.connectivityObj = self.create3DColFlangeBeamWeb() - - else: - self.connectivityObj = self.create3DBeamWebBeamWeb() - - self.display_3DModel("Model","gradient_bg") - else: - self.display.EraseAll() - - elif self.mainmodule == "Moment Connection": - - if self.connection == KEY_DISP_BEAMCOVERPLATE or self.connection == KEY_DISP_BEAMCOVERPLATEWELD: - if flag is True: - - self.CPObj = self.createBBCoverPlateCAD() - - self.display_3DModel("Model", "gradient_bg") - else: - self.display.EraseAll() - - elif self.connection == KEY_DISP_BB_EP_SPLICE: - if flag is True: - - self.CPObj = self.createBBEndPlateCAD() - - self.display_3DModel("Model", "gradient_bg") - - else: - self.display.EraseAll() - - elif self.connection == KEY_DISP_BCENDPLATE: - if flag is True: - - self.CPObj = self.createBCEndPlateCAD() - - self.display_3DModel("Model", "gradient_bg") - - else: - self.display.EraseAll() - - elif self.connection == KEY_DISP_COLUMNCOVERPLATE or self.connection == KEY_DISP_COLUMNCOVERPLATEWELD: - if flag is True: - - self.CPObj = self.createCCCoverPlateCAD() - - self.display_3DModel("Model", "gradient_bg") - - else: - self.display.EraseAll() - - elif self.connection == KEY_DISP_COLUMNENDPLATE: - if flag is True: - self.CEPObj = self.createCCEndPlateCAD() - - self.display_3DModel("Model", "gradient_bg") - else: - self.display.EraseAll() - - elif self.connection == KEY_DISP_BASE_PLATE: - - if flag is True: - self.BPObj = self.createBasePlateCAD() - - self.display_3DModel("Model", "gradient_bg") - - else: - self.display.EraseAll() - - else: - if self.connection == KEY_DISP_TENSION_BOLTED or self.connection == KEY_DISP_TENSION_WELDED: - - if flag is True: - self.TObj = self.createTensionCAD() - - self.display_3DModel("Model", "gradient_bg") - - else: - self.display.EraseAll() - - # def call_saveOutputs(self): # Done - # return self.call_calculation(self.uiObj) - # - # def call2D_Drawing(self, viKEY_DISP_BASE_PLATEew, fileName, folder): # Rename function with call_view_images() - # ''' This routine saves the 2D SVG image as per the connectivity selected - # SVG image created through svgwrite package which takes design INPUT and OUTPUT parameters from Finplate GUI. - # ''' - # if view == "All": - # - # self.callDesired_View(fileName, view, folder) - # # self.display.set_bg_gradient_color(255, 255, 255, 255, 255, 255) - # # - # # data = os.path.join(str(folder), "images_html", "3D_Model.png") - # # - # # self.display.ExportToImage(data) - # # - # # # self.display.set_bg_gradient_color(51, 51, 102, 150, 150, 170) - # # self.display.View_Iso() - # # self.display.FitAll() - # - # else: - # - # f = open(fileName, 'w') - # - # self.callDesired_View(fileName, view, folder) - # f.close() - # - # def callDesired_View(self, fileName, view, folder): - # - # if self.connection == "Fin Plate": - # finCommonObj = FinCommonData(self.uiObj, self.resultObj, self.dictbeamdata, self.dictcoldata, folder) - # finCommonObj.saveToSvg(str(fileName), view) - # elif self.connection == "Endplate": - # endCommonObj = EndCommonData(self.uiObj, self.resultObj, self.dictbeamdata, self.dictcoldata, folder) - # endCommonObj.save_to_svg(str(fileName), view) - # elif self.connection == "cleatAngle": - # cleatCommonObj = cleatCommonData(self.uiObj, self.resultObj, self.dictbeamdata, self.dictcoldata, - # self.dictangledata, folder) - # cleatCommonObj.save_to_svg(str(fileName), view) - # else: - # seatCommonObj = SeatCommonData(self.uiObj, self.resultObj, self.dictbeamdata, self.dictcoldata, - # self.dictangledata, self.dicttopangledata, folder) - # seatCommonObj.save_to_svg(str(fileName), view) - # - # def call_saveMessages(self): # Done - # - # if self.connection == "Fin Plate": - # fileName = os.path.join("Connections", "Shear", "Fin Plate", "fin.log") - # - # elif self.connection == "Endplate": - # fileName = os.path.join("Connections", "Shear", "Endplate", "end.log") - # - # elif self.connection == "cleatAngle": - # fileName = os.path.join("Connections", "Shear", "cleatAngle", "cleat.log") - # - # else: - # fileName = os.path.join("Connections", "Shear", "SeatedAngle", "seatangle.log") - # - # return fileName - # - # def call_designReport(self, htmlfilename, profileSummary): - # - # fileName = str(htmlfilename) - # - # if self.connection == "Fin Plate": - # fin_save_html(self.resultObj, self.uiObj, self.dictbeamdata, self.dictcoldata, profileSummary, - # htmlfilename, self.folder) - # elif self.connection == "Endplate": - # end_save_html(self.resultObj, self.uiObj, self.dictbeamdata, self.dictcoldata, profileSummary, - # htmlfilename, self.folder) - # elif self.connection == "cleatAngle": - # cleat_save_html(self.resultObj,self.uiObj,self.dictbeamdata,self.dictcoldata,self.dictangledata, - # profileSummary,htmlfilename, self.folder) - # else: - # self.sa_report = ReportGenerator(self.sa_calc_obj) - # self.sa_report.save_html(profileSummary,htmlfilename,self.folder) - # - # def load_userProfile(self): - # # TODO load_userProfile - deepa - # pass - # - # - # def save_userProfile(self, profile_summary, fileName): - # # TODO save_userProfile - deepa - # filename = str(fileName) - # - # infile = open(filename, 'w') - # json.dump(profile_summary, infile) - # infile.close() - # pass - # - # def save_CADimages(self): # png,jpg and tiff - # # TODO save_CADimages - deepa - # pass - - def create2Dcad(self): - ''' Returns the 3D model of finplate depending upon component - ''' - - final_model = None - cadlist = [] - - if self.mainmodule == "Shear Connection": - if self.component == "Beam": - final_model = self.connectivityObj.get_beamModel() - elif self.component == "Column": - final_model = self.connectivityObj.get_columnModel() - elif self.component == "Plate": - cadlist = [self.connectivityObj.weldModelLeft, self.connectivityObj.weldModelRight, - self.connectivityObj.plateModel] + self.connectivityObj.nut_bolt_array.get_models() - elif self.component == "cleatAngle": - cadlist = [self.connectivityObj.angleModel, self.connectivityObj.angleLeftModel] + \ - self.connectivityObj.nut_bolt_array.get_models() - elif self.component == "SeatAngle": - cadlist = [self.connectivityObj.topclipangleModel, self.connectivityObj.angleModel] + \ - self.connectivityObj.nut_bolt_array.get_models() - else: - cadlist = self.connectivityObj.get_models() - - elif self.mainmodule == "Moment Connection": - if self.connection == KEY_DISP_BEAMCOVERPLATE or self.connection == KEY_DISP_BEAMCOVERPLATEWELD: - if self.component == "Beam": - if self.connection == KEY_DISP_BEAMCOVERPLATE: - final_model = self.CPObj.get_only_beams_Models() - else: - final_model = self.CPObj.get_beam_models() - elif self.component == "Connector": - if self.connection == KEY_DISP_BEAMCOVERPLATE: - cadlist = [self.CPObj.get_flangewebplatesModel(), self.CPObj.get_nut_bolt_arrayModels()] - if self.B.preference != 'Outside': - cadlist.insert(1, self.CPObj.get_innetplatesModels()) - else: - cadlist = [self.CPObj.get_plate_models(), self.CPObj.get_welded_modules()] - else: - cadlist = self.CPObj.get_models() - - elif self.connection == KEY_DISP_BB_EP_SPLICE: - - if self.component == "Beam": - final_model = self.CPObj.get_beam_models() - - elif self.component == "Connector": - - final_model = self.CPObj.get_connector_models() - - else: - final_model = self.CPObj.get_models() - - elif self.connection == KEY_DISP_BCENDPLATE: - - # self.ExtObj = self.create_extended_both_ways() - if self.component == "Column": - final_model = self.CPObj.get_column_models() - - elif self.component == "Beam": - final_model = self.CPObj.get_beam_models() - - elif self.component == "Connector": - final_model = self.CPObj.get_connector_models() - - else: - final_model = self.CPObj.get_models() - - - elif self.connection == KEY_DISP_COLUMNCOVERPLATE or self.connection == KEY_DISP_COLUMNCOVERPLATEWELD: - if self.component == "Column": - if self.connection == KEY_DISP_COLUMNCOVERPLATE: - final_model = self.CPObj.get_only_column_models() - else: - final_model = self.CPObj.get_column_models() - elif self.component == "Cover Plate": - if self.connection == KEY_DISP_COLUMNCOVERPLATE: - cadlist = [self.CPObj.get_plate_models(), self.CPObj.get_nut_bolt_models()] - else: - cadlist = [self.CPObj.get_plate_models(), self.CPObj.get_welded_modules()] - else: - cadlist = self.CPObj.get_models() - - elif self.connection == KEY_DISP_COLUMNENDPLATE: - if self.component == "Column": - final_model = self.CEPObj.get_column_models() - elif self.component == "Connector": - plates = self.CEPObj.get_plate_models() - welds = self.CEPObj.get_weld_models() - nutBolts = self.CEPObj.get_nut_bolt_models() - cadlist = [plates, welds, nutBolts] - else: - final_model = self.CEPObj.get_models() - - elif self.connection == KEY_DISP_BASE_PLATE: - if self.component == "Column": - final_model = self.BPObj.get_column_model() - elif self.component == "Connector": - plate = self.BPObj.get_plate_connector_models() - weld = self.BPObj.get_welded_models() - nut_bolt = self.BPObj.get_nut_bolt_array_models() - cadlist = [plate, weld, nut_bolt] - else: - final_model = self.BPObj.get_models() - - elif self.mainmodule == "Member": - if self.connection == KEY_DISP_TENSION_BOLTED or self.connection == KEY_DISP_TENSION_WELDED: - if self.component == "Member": - final_model = self.TObj.get_members_models() - elif self.component == "Plate": - if self.connection == KEY_DISP_TENSION_BOLTED: - cadlist = [self.TObj.get_plates_models(), self.TObj.get_nut_bolt_array_models()] - else: - cadlist = [self.TObj.get_plates_models(), self.TObj.get_welded_models()] - else: - # print(type(self.TObj.shape)) - final_model = self.TObj.shape - # cadlist = self.TObj.get_models() #TODO: get_models() in BoltedCAD.py and WeldedCAD.py is not returning anything right now. - - if cadlist and len(cadlist) > 1: - final_model = cadlist[0] - for model in cadlist[1:]: - final_model = BRepAlgoAPI_Fuse(model, final_model).Shape() - - return final_model - - # if self.component == "Beam": - # # final_model = self.connectivityObj.get_beamModel() - # final_model = Obj.get_beamModel() - # - # elif self.component == "Column": - # # final_model = self.connectivityObj.columnModel - # final_model = Obj.columnModel - # - # elif self.component == "Plate": - # # cadlist = [self.connectivityObj.weldModelLeft, - # # self.connectivityObj.weldModelRight, - # # self.connectivityObj.plateModel] + self.connectivityObj.nut_bolt_array.get_models() - # cadlist = [Obj.weldModelLeft, - # Obj.weldModelRight, - # Obj.plateModel] + Obj.nut_bolt_array.get_models() - # final_model = cadlist[0] - # for model in cadlist[1:]: - # final_model = BRepAlgoAPI_Fuse(model, final_model).Shape() - # else: - # # cadlist = self.connectivityObj.get_models() - # cadlist = Obj.get_models() - # if self.connection == KEY_DISP_BASE_PLATE: - # return cadlist - # final_model = cadlist[0] - # for model in cadlist[1:]: - # final_model = BRepAlgoAPI_Fuse(model, final_model).Shape() - -# if __name__!= "__main__": -# -# CommonDesignLogic() - - - diff --git a/cad/items/bolt.py b/cad/items/bolt.py deleted file mode 100644 index d2d2fa91e..000000000 --- a/cad/items/bolt.py +++ /dev/null @@ -1,112 +0,0 @@ -''' -Created on 29-Nov-2014 - -@author: deepa -''' -import numpy -from cad.items.ModelUtils import getGpPt, getGpDir, makeEdgesFromPoints, makeWireFromEdges, makePrismFromFace, makeFaceFromWire -import math -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeCylinder -from OCC.Core.gp import gp_Ax2 -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse - - -class Bolt(object): - ''' - - a3 X-------------------+ a2 - X X|X - X X | X - X X | X - X X | X - X X | X - X X | X - X X 60 | X -a4 X XXXXXXXXXXXXXXXXX a1 - X X - X X - XX X - X X - X X - X X - X X - X-------------------X - a6 - a5 - - - ''' - - def __init__(self, R, T, H, r): - self.R = R - self.H = H - self.T = T - self.r = r - self.origin = None - self.uDir = None - self.shaftDir = None - self.vDir = None - self.a1 = None - self.a2 = None - self.a3 = None - self.a4 = None - self.a5 = None - self.a6 = None - self.points = [] - - def place(self, origin, uDir, shaftDir): - self.origin = origin - self.uDir = uDir - self.shaftDir = shaftDir - self.compute_params() - - def getPoint(self, theta): - theta = math.radians(theta) - point = self.origin + (self.R * math.cos(theta)) * self.uDir + (self.R * math.sin(theta)) * self.vDir - return point - - def compute_params(self): - self.vDir = numpy.cross(self.shaftDir, self.uDir) - self.a1 = self.getPoint(0) - self.a2 = self.getPoint(60) - self.a3 = self.getPoint(120) - self.a4 = self.getPoint(180) - self.a5 = self.getPoint(240) - self.a6 = self.getPoint(300) - self.points = [self.a1, self.a2, self.a3, self.a4, self.a5, self.a6] - - def create_model(self): - - edges = makeEdgesFromPoints(self.points) - wire = makeWireFromEdges(edges) - aFace = makeFaceFromWire(wire) - extrudeDir = -self.T * self.shaftDir # extrudeDir is a numpy array - boltHead = makePrismFromFace(aFace, extrudeDir) - cylOrigin = self.origin - boltCylinder = BRepPrimAPI_MakeCylinder(gp_Ax2(getGpPt(cylOrigin), getGpDir(self.shaftDir)), self.r, self.H).Shape() - - whole_Bolt = BRepAlgoAPI_Fuse(boltHead, boltCylinder).Shape() - - return whole_Bolt - -if __name__ == '__main__': - - from OCC.Display.SimpleGui import init_display - display, start_display, add_menu, add_function_to_menu = init_display() - - R = 8 - H = 10 - T = 5 - r = 3 - - origin = numpy.array([0.,0.,0.]) - uDir = numpy.array([1.,0.,0.]) - shaftDir = numpy.array([0.,0.,1.]) - - bolt = Bolt(R, T, H, r) - _place = bolt.place(origin, uDir, shaftDir) - point = bolt.compute_params() - prism = bolt.create_model() - display.DisplayShape(prism, update=True) - display.DisableAntiAliasing() - start_display() \ No newline at end of file diff --git a/cad/items/nut.py b/cad/items/nut.py deleted file mode 100644 index 73ea95ab4..000000000 --- a/cad/items/nut.py +++ /dev/null @@ -1,109 +0,0 @@ -''' -Created on 12-Dec-2014 -NUT COMMENT -@author: deepa -''' - -import math -import numpy -from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut -from cad.items.ModelUtils import getGpPt, getGpDir, makeEdgesFromPoints, makeWireFromEdges, makePrismFromFace, makeFaceFromWire -from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeCylinder -from OCC.Core.gp import gp_Ax2 - - -class Nut(object): - - ''' - a3 X-------------------+ a2 - X X|X - X X | X - X X | X - X X | X - X X | X - X X | X - X X 60 | X -a4 X XXXXXXXXXXXXXXXXX a1 - X X - X X - XX X - X X - X X - X X - X X - X-------------------X - a6 - a5 - - ''' - - def __init__(self, R, T, H, innerR1): - self.R = R - self.H = H - self.T = T - self.r1 = innerR1 - # self.r2 = outerR2 - self.sec_origin = numpy.array([0, 0, 0]) - self.uDir = numpy.array([1.0, 0, 0]) - self.wDir = numpy.array([0.0, 0, 1.0]) - self.compute_params() - - def place(self, sec_origin, uDir, wDir): - self.sec_origin = sec_origin - self.uDir = uDir - self.wDir = wDir - self.compute_params() - - def getPoint(self, theta): - theta = math.radians(theta) - point = self.sec_origin + (self.R * math.cos(theta)) * self.uDir + (self.R * math.sin(theta)) * self.vDir - return point - - def compute_params(self): - - self.vDir = numpy.cross(self.wDir, self.uDir) - self.a1 = self.getPoint(0) - self.a2 = self.getPoint(60) - self.a3 = self.getPoint(120) - self.a4 = self.getPoint(180) - self.a5 = self.getPoint(240) - self.a6 = self.getPoint(300) - self.points = [self.a1, self.a2, self.a3, self.a4, self.a5, self.a6] - - def create_model(self): - - edges = makeEdgesFromPoints(self.points) - wire = makeWireFromEdges(edges) - aFace = makeFaceFromWire(wire) - extrudeDir = self.T * self.wDir # extrudeDir is a numpy array - prism = makePrismFromFace(aFace, extrudeDir) - - cylOrigin = self.sec_origin - innerCyl = BRepPrimAPI_MakeCylinder(gp_Ax2(getGpPt(cylOrigin), getGpDir(self.wDir)), self.r1, self.H).Shape() - - result_shape = BRepAlgoAPI_Cut(prism, innerCyl).Shape() - - return result_shape - - -if __name__ == '__main__': - - from OCC.Display.SimpleGui import init_display - display, start_display, add_menu, add_function_to_menu = init_display() - - R = 10 - T = 8 - H = 10 - innerR1 = 5 - - origin = numpy.array([0.,0.,0.]) - uDir = numpy.array([1.,0.,0.]) - wDir = numpy.array([0.,0.,1.]) - - nut = Nut(R, T, H, innerR1) - _place = nut.place(origin, uDir, wDir) - point = nut.compute_params() - prism = nut.create_model() - display.DisplayShape(prism, update=True) - display.DisableAntiAliasing() - start_display() \ No newline at end of file diff --git a/design_report/reportGenerator_latex.py b/design_report/reportGenerator_latex.py deleted file mode 100644 index 8efb59f4e..000000000 --- a/design_report/reportGenerator_latex.py +++ /dev/null @@ -1,396 +0,0 @@ -from builtins import str -import time -from Report_functions import * -import math -from utils.common.common_calculation import * -# from Common import * -import os -# from utils.common import component -from pylatex import Document, Section, Subsection -from pylatex.utils import italic, bold -#import pdflatex -import sys -import datetime -import pylatex as pyl -from pylatex.basic import TextColor -from pylatex import Document, Section, Subsection, Tabular, Tabularx,MultiColumn, LongTable, LongTabularx, LongTabu, MultiRow, StandAloneGraphic -from pylatex import Math, TikZ, Axis, Plot, Figure, Matrix, Alignat, TextColor -from pylatex import Document, Section, Subsection, Tabular, Tabularx,MultiColumn, LongTable, LongTabularx, LongTabu, MultiRow, StandAloneGraphic -from pylatex import Math, TikZ, Axis, Plot, Figure, Matrix, Alignat, TextColor -from pylatex import Document, Section, Subsection, Tabular, Tabularx,MultiColumn, LongTable, LongTabularx, LongTabu,\ - MultiRow, StandAloneGraphic -from pylatex import Math, TikZ, Axis, Plot, Figure, Matrix, Alignat, SubFigure -from pylatex.utils import italic -#from pdflatex import PDFLaTeX -import os -from pylatex.base_classes import Environment, CommandBase, Arguments -from pylatex.package import Package -from pylatex import Document, PageStyle, Head, MiniPage, Foot, LargeText, \ - MediumText, LineBreak, simple_page_number, NewPage - -from pylatex.utils import bold - -class CreateLatex(Document): - - def __init__(self): - super().__init__() - - - def save_latex(self, uiObj, Design_Check, reportsummary, filename, rel_path, Disp_2d_image, Disp_3d_image, module=''): - companyname = str(reportsummary["ProfileSummary"]['CompanyName']) - companylogo = str(reportsummary["ProfileSummary"]['CompanyLogo']) - groupteamname = str(reportsummary["ProfileSummary"]['Group/TeamName']) - designer = str(reportsummary["ProfileSummary"]['Designer']) - projecttitle = str(reportsummary['ProjectTitle']) - subtitle = str(reportsummary['Subtitle']) - jobnumber = str(reportsummary['JobNumber']) - client = str(reportsummary['Client']) - - does_design_exist = reportsummary['does_design_exist'] - osdagheader = '/ResourceFiles/images/Osdag_header_report.png' - # Add document header - geometry_options = {"top": "5cm", "hmargin": "2cm", "headheight": "100pt", "footskip": "100pt", "bottom":"5cm"} - doc = Document(geometry_options=geometry_options,indent=False) - doc.packages.append(Package('amsmath')) - doc.packages.append(Package('graphicx')) - doc.packages.append(Package('needspace')) - doc.append(pyl.Command('fontsize', arguments= [8,12])) - doc.append(pyl.Command('selectfont')) - - - doc.add_color('OsdagGreen', 'RGB', '153,169,36') - doc.add_color('PassColor','RGB', '153,169,36') - doc.add_color('Red', 'RGB', '255,0,0') - doc.add_color('Green', 'RGB', '0,200,0') - doc.add_color('FailColor','HTML','933A16') - header = PageStyle("header") - # Create center header - with header.create(Head("C")): - with header.create(Tabularx('|l|p{4cm}|l|X|')) as table: - table.add_hline() - # MultiColumn(4) - table.add_row((MultiColumn(2, align='|c|', data=('' if companylogo is'' else StandAloneGraphic(image_options="height=0.95cm", - filename=companylogo))), - MultiColumn(2, align='|c|', - data=['Created with',StandAloneGraphic(image_options="width=4.0cm,height=1cm", - filename=rel_path + osdagheader)]),)) - table.add_hline() - table.add_row(('Company Name', companyname, 'Project Title', projecttitle), color='OsdagGreen') - table.add_hline() - table.add_row(('Group/Team Name', groupteamname, 'Subtitle', subtitle), color='OsdagGreen') - table.add_hline() - table.add_row(('Designer', designer, 'Job Number', jobnumber), color='OsdagGreen') - table.add_hline() - table.add_row(('Date', time.strftime("%d /%m /%Y"), 'Client', client), color='OsdagGreen') - table.add_hline() - - - - # Create right footer - with header.create(Foot("R")): - header.append(NoEscape(r'Page \thepage')) - # - # doc.preamble.append(header) - # doc.change_document_style("header") - - # Add Heading - # with doc.create(MiniPage(align='c')): - - - doc.preamble.append(header) - doc.change_document_style("header") - with doc.create(Section('Input Parameters')): - with doc.create(LongTable('|p{5cm}|p{2.5cm}|p{1.5cm}|p{3cm}|p{3.5cm}|', row_height=1.2)) as table: - table.add_hline() - for i in uiObj: - # row_cells = ('9', MultiColumn(3, align='|c|', data='Multicolumn not on left')) - if i == "Selected Section Details" or i==KEY_DISP_ANGLE_LIST or i==KEY_DISP_TOPANGLE_LIST or i==KEY_DISP_CLEAT_ANGLE_LIST: - # if type(uiObj[i]) == list: - continue - if type(uiObj[i]) == dict: - table.add_hline() - sectiondetails = uiObj[i] - image_name = sectiondetails[KEY_DISP_SEC_PROFILE] - - Img_path = '/ResourceFiles/images/'+image_name+'.png' - if (len(sectiondetails))% 2 == 0: - # merge_rows = int(round_up(len(sectiondetails),2)/2 + 2) - merge_rows = int((len(sectiondetails)/2)) +2 - else: - merge_rows = round_up((len(sectiondetails)/2),2) - if (len(sectiondetails))% 2 == 0: - sectiondetails['']='' - - a = list(sectiondetails.keys()) - # index=0 - for x in range(1, (merge_rows + 1)): - # table.add_row("Col.Det.",i,columndetails[i]) - if x == 1: - table.add_row( - (MultiRow(merge_rows, data=StandAloneGraphic(image_options="width=5cm,height=5cm", - filename=rel_path + Img_path)), - MultiColumn(2, align='|c|', data=a[x]), - MultiColumn(2, align='|c|', data=sectiondetails[a[x]]),)) - elif x <= 4: - table.add_row(('', MultiColumn(2, align='|c|', data=NoEscape(a[x])), - MultiColumn(2, align='|c|', data=NoEscape(sectiondetails[a[x]])),)) - else: - table.add_row(('', NoEscape(a[x]), sectiondetails[a[x]], NoEscape(a[merge_rows + x - 4]), - sectiondetails[a[merge_rows + x - 4]],)) - table.add_hline(2, 5) - elif uiObj[i] == "TITLE": - table.add_hline() - table.add_row((MultiColumn(5, align='|c|', data=bold(i), ),)) - table.add_hline() - elif i == 'Section Size*': - table.add_hline() - table.add_row((MultiColumn(3, align='|c|', data=i, ),MultiColumn(2, align='|c|', data="Ref List of Input Section"),)) - table.add_hline() - elif len(str(uiObj[i])) > 55 and type(uiObj[i]) != pyl.math.Math: - str_len = len(str(uiObj[i])) - loop_len = round_up((str_len / 55), 1, 1) - for j in range(1, loop_len + 1): - b = 55 * j + 1 - if j == 1: - table.add_row( - (MultiColumn(3, align='|c|', data=MultiRow(loop_len,data=i)), MultiColumn(2, align='|c|', data=uiObj[i][0:b]),)) - else: - table.add_row( - (MultiColumn(3, align='|c|', data=MultiRow(loop_len,data="")), - MultiColumn(2, align='|c|', data=uiObj[i][b - 55:b]),)) - table.add_hline() - else: - table.add_hline() - table.add_row((MultiColumn(3, align='|c|', data=NoEscape(i)), MultiColumn(2, align='|c|', data=uiObj[i]),)) - table.add_hline() - for i in uiObj: - if i == 'Section Size*' or i == KEY_DISP_ANGLE_LIST or i == KEY_DISP_TOPANGLE_LIST or i==KEY_DISP_CLEAT_ANGLE_LIST: - with doc.create(Subsection("List of Input Section")): - # with doc.create(LongTable('|p{8cm}|p{8cm}|', row_height=1.2)) as table: - with doc.create(Tabularx('|p{4cm}|X|', row_height=1.2)) as table: - table.add_hline() - table.add_row((MultiColumn(1, align='|c|', data=i, ), - MultiColumn(1, align='|X|', data=uiObj[i].strip("[]")),)) - # str_len = len(uiObj[i]) - # loop_len = round_up((str_len/100),1,1) - # table.add_hline() - # for j in range(1,loop_len+1): - # b= 100*j+1 - # if j ==1: - # table.add_row((MultiColumn(1, align='|c|', data=i, ), - # MultiColumn(1, align='|X|', data=uiObj[i][0:b]),)) - # else: - # table.add_row((MultiColumn(1, align='|c|', data=" ", ), - # MultiColumn(1, align='|X|', data=uiObj[i][b-100:b]),)) - table.add_hline() - - doc.append(pyl.Command('Needspace', arguments=NoEscape(r'10\baselineskip'))) - doc.append(NewPage()) - count = 0 - with doc.create(Section('Design Checks')): - with doc.create(Tabularx(r'|>{\centering}p{12.5cm}|>{\centering\arraybackslash}X|', row_height=1.2)) as table: - table.add_hline() - # Fail = TextColor("FailColor", bold("Fail")) - # Pass = TextColor("PassColor", bold("Pass")) - - - if does_design_exist != True: - table.add_row(bold('Design Status'),color_cell("Red",bold("Fail"))) - else: - table.add_row(bold('Design Status'),color_cell("OsdagGreen",bold("Pass"))) - table.add_hline() - - - - for check in Design_Check: - - if check[0] == 'SubSection': - if count >=1: - # doc.append(NewPage()) - doc.append(pyl.Command('Needspace', arguments=NoEscape(r'10\baselineskip'))) - with doc.create(Subsection(check[1])): -######################### - # if uiObj== "WELDImage": - # table.add_hline() - # table.add_row((MultiColumn(5, align='|c|', data=bold(i), ),)) - # table.add_hline() - # else: -######################### - with doc.create(LongTable(check[2], row_height=1.2)) as table: # todo anjali remove - table.add_hline() - table.add_row(('Check', 'Required', 'Provided', 'Remarks'), color='OsdagGreen') - table.add_hline() - table.end_table_header() - table.add_hline() - count = count + 1 - elif check[0] == "Selected": - if count >=1: - # doc.append(NewPage()) - doc.append(pyl.Command('Needspace', arguments=NoEscape(r'10\baselineskip'))) - with doc.create(Subsection(check[1])): - with doc.create(LongTable(check[2], row_height=1.2)) as table: - table.add_hline() - for i in uiObj: - # row_cells = ('9', MultiColumn(3, align='|c|', data='Multicolumn not on left')) - - print(i) - if type(uiObj[i]) == dict and i == 'Selected Section Details': - table.add_hline() - sectiondetails = uiObj[i] - image_name = sectiondetails[KEY_DISP_SEC_PROFILE] - Img_path = '/ResourceFiles/images/' + image_name + '.png' - if (len(sectiondetails)) % 2 == 0: - # merge_rows = int(round_up(len(sectiondetails),2)/2 + 2) - merge_rows = int(round_up((len(sectiondetails) / 2), 1, 0) + 2) - else: - merge_rows = int(round_up((len(sectiondetails) / 2), 1, 0) + 1) - print('Hi', len(sectiondetails) / 2, round_up(len(sectiondetails), 2) / 2, - merge_rows) - if (len(sectiondetails)) % 2 == 0: - sectiondetails[''] = '' - a = list(sectiondetails.keys()) - # index=0 - for x in range(1, (merge_rows + 1)): - # table.add_row("Col.Det.",i,columndetails[i]) - if x == 1: - table.add_row( - (MultiRow(merge_rows, - data=StandAloneGraphic(image_options="width=5cm,height=5cm", - filename=rel_path + Img_path)), - MultiColumn(2, align='|c|', data=NoEscape(a[x])), - MultiColumn(2, align='|c|', data=NoEscape(sectiondetails[a[x]])),)) - elif x <= 4: - table.add_row(('', MultiColumn(2, align='|c|', data=NoEscape(a[x])), - - MultiColumn(2, align='|c|', data=sectiondetails[a[x]]),)) - else: - table.add_row(('', NoEscape(a[x]), sectiondetails[a[x]], NoEscape(a[merge_rows + x - 4]), - sectiondetails[a[merge_rows + x - 4]],)) - - table.add_hline(2, 5) - table.add_hline() - count = count + 1 - else: - - if check[3] == 'Fail': - table.add_row((NoEscape(check[0])), check[1], check[2], TextColor("Red", bold(check[3]))) - else: - table.add_row((NoEscape(check[0])), check[1], check[2], TextColor("OsdagGreen", bold(check[3]))) - table.add_hline() - - # 2D images - if len(Disp_2d_image) != 0: - - if module == KEY_DISP_BCENDPLATE or module == KEY_DISP_BB_EP_SPLICE: - if does_design_exist and sys.platform != 'darwin': - doc.append(NewPage()) - weld_details = rel_path + Disp_2d_image[0] - detailing_details = rel_path + Disp_2d_image[1] - stiffener_details = rel_path + Disp_2d_image[2] - - with doc.create(Section('2D Drawings (Typical)')): - - with doc.create(Figure()) as image: - image.add_image(weld_details, width=NoEscape(r'0.7\textwidth'), placement=NoEscape(r'\centering')) - image.add_caption('Typical Weld Details -- Beam to End Plate Connection') - # doc.append(NewPage()) - - with doc.create(Figure()) as image_2: - image_2.add_image(detailing_details, width=NoEscape(r'0.7\textwidth'), placement=NoEscape(r'\centering')) - image_2.add_caption('Typical Detailing') - # doc.append(NewPage()) - - with doc.create(Figure()) as image_3: - image_3.add_image(stiffener_details, width=NoEscape(r'0.9\textwidth'), placement=NoEscape(r'\centering')) - image_3.add_caption('Typical Stiffener Details') - # doc.append(NewPage()) - - elif module == KEY_DISP_BASE_PLATE: - if does_design_exist and sys.platform != 'darwin': - doc.append(NewPage()) - bp_sketch = rel_path + Disp_2d_image[0] - bp_detailing = rel_path + Disp_2d_image[1] - bp_weld = rel_path + Disp_2d_image[2] - bp_anchor = rel_path + Disp_2d_image[3] - bp_key = rel_path + Disp_2d_image[4] - - with doc.create(Section('2D Drawings (Typical)')): - with doc.create(Figure()) as image_1: - image_1.add_image(bp_sketch, width=NoEscape(r'1.0\textwidth'), placement=NoEscape(r'\centering')) - image_1.add_caption('Typical Base Plate Details') - # doc.append(NewPage()) - - with doc.create(Figure()) as image_2: - image_2.add_image(bp_detailing, width=NoEscape(r'1.0\textwidth'), placement=NoEscape(r'\centering')) - image_2.add_caption('Typical Base Plate Detailing') - # doc.append(NewPage()) - - with doc.create(Figure()) as image_3: - image_3.add_image(bp_weld, width=NoEscape(r'1.0\textwidth'), placement=NoEscape(r'\centering')) - image_3.add_caption('Typical Weld Details') - # doc.append(NewPage()) - - with doc.create(Figure()) as image_4: - image_4.add_image(bp_anchor, width=NoEscape(r'0.5\textwidth'), placement=NoEscape(r'\centering')) - image_4.add_caption('Typical Anchor Bolt Details') - # doc.append(NewPage()) - - if len(Disp_2d_image[-1]) > 0: - with doc.create(Figure()) as image_5: - image_5.add_image(bp_key, width=NoEscape(r'0.9\textwidth'), placement=NoEscape(r'\centering')) - image_5.add_caption('Typical Shear Key Details') - # doc.append(NewPage()) - - if does_design_exist and sys.platform != 'darwin': - doc.append(NewPage()) - Disp_top_image = "/ResourceFiles/images/top.png" - Disp_side_image = "/ResourceFiles/images/side.png" - Disp_front_image = "/ResourceFiles/images/front.png" - view_3dimg_path = rel_path + Disp_3d_image - view_topimg_path = rel_path + Disp_top_image - view_sideimg_path = rel_path + Disp_side_image - view_frontimg_path = rel_path + Disp_front_image - with doc.create(Section('3D Views')): - with doc.create(Tabularx(r'|>{\centering}X|>{\centering\arraybackslash}X|', row_height=1.2)) as table: - view_3dimg_path = rel_path + Disp_3d_image - view_topimg_path = rel_path + Disp_top_image - view_sideimg_path = rel_path + Disp_side_image - view_frontimg_path = rel_path + Disp_front_image - table.add_hline() - table.add_row([StandAloneGraphic(image_options="height=4cm",filename=view_3dimg_path), - StandAloneGraphic(image_options="height=4cm",filename=view_topimg_path)]) - table.add_row('(a) 3D View', '(b) Top View') - table.add_hline() - table.add_row([StandAloneGraphic(image_options="height=4cm", filename=view_sideimg_path), - StandAloneGraphic(image_options="height=4cm", filename=view_frontimg_path)]) - table.add_row('(c) Side View', '(d) Front View') - table.add_hline() - # with doc.create(Figure(position='h!')) as view_3D: - # view_3dimg_path = rel_path + Disp_3d_image - # # view_3D.add_image(filename=view_3dimg_path, width=NoEscape(r'\linewidth')) - # - # view_3D.add_image(filename=view_3dimg_path,width=NoEscape(r'\linewidth,height=6.5cm')) - # - # view_3D.add_caption('3D View') - - with doc.create(Section('Design Log')): - doc.append(pyl.Command('Needspace', arguments=NoEscape(r'10\baselineskip'))) - logger_msgs=reportsummary['logger_messages'].split('\n') - for msg in logger_msgs: - if('WARNING' in msg): - colour='blue' - elif('INFO' in msg): - colour='OsdagGreen' - elif('ERROR' in msg): - colour='red' - else: - continue - doc.append(TextColor(colour,'\n'+msg)) - try: - doc.generate_pdf(filename, compiler='pdflatex', clean_tex=False) - except: - pass - -def color_cell(cellcolor,celltext): - string = NoEscape(r'\cellcolor{'+cellcolor+r'}{'+celltext+r'}') - return string diff --git a/design_type/compression_member/compression.py b/design_type/compression_member/compression.py deleted file mode 100644 index 4828e3aa3..000000000 --- a/design_type/compression_member/compression.py +++ /dev/null @@ -1,419 +0,0 @@ -from design_type.member import Member -from Common import * -from utils.common.component import ISection, Material -from utils.common.load import Load -from utils.common.Section_Properties_Calculator import I_sectional_Properties - - -class Compression(Member): - - ############################################### - # Design Preference Functions Start - ############################################### - def tab_list(self): - """ - - :return: This function returns the list of tuples. Each tuple will create a tab in design preferences, in the - order they are appended. Format of the Tuple is: - [Tab Title, Type of Tab, function for tab content) - Tab Title : Text which is displayed as Title of Tab, - Type of Tab: There are Three types of tab layouts. - Type_TAB_1: This have "Add", "Clear", "Download xlsx file" "Import xlsx file" - TYPE_TAB_2: This contains a Text box for side note. - TYPE_TAB_3: This is plain layout - function for tab content: All the values like labels, input widgets can be passed as list of tuples, - which will be displayed in chosen tab layout - - """ - tabs = [] - - t1 = (KEY_DISP_COLSEC, TYPE_TAB_1, self.tab_section) - tabs.append(t1) - - t5 = ("Design", TYPE_TAB_2, self.design_values) - tabs.append(t5) - - return tabs - - def tab_value_changed(self): - """ - - :return: This function is used to update the values of the keys in design preferences, - which are dependent on other inputs. - It returns list of tuple which contains, tab name, keys whose values will be changed, - function to change the values and arguments for the function. - - [Tab Name, [Argument list], [list of keys to be updated], input widget type of keys, change_function] - - Here Argument list should have only one element. - Changing of this element,(either changing index or text depending on widget type), - will update the list of keys (this can be more than one). - TODO: input widget type of keys (3rd element) is no longer required. needs to be removed - - """ - change_tab = [] - # - # t1 = (KEY_DISP_COLSEC, [KEY_SECSIZE, KEY_SEC_MATERIAL], - # [KEY_SECSIZE_SELECTED, KEY_SEC_FY, KEY_SEC_FU, 'Label_1', 'Label_2', 'Label_3', 'Label_4', 'Label_5', - # 'Label_7', 'Label_11', 'Label_12', 'Label_13', 'Label_14', 'Label_15', 'Label_16', 'Label_17', - # 'Label_18', 'Label_19', 'Label_20', 'Label_21', 'Label_22', KEY_IMAGE], TYPE_TEXTBOX, - # self.get_new_angle_section_properties) - # change_tab.append(t1) - # - # t2 = (DISP_TITLE_ANGLE, ['Label_1', 'Label_2', 'Label_3', 'Label_0'], - # ['Label_7', 'Label_8', 'Label_9', 'Label_10', 'Label_11', 'Label_12', 'Label_13', 'Label_14', - # 'Label_15', - # 'Label_16', 'Label_17', 'Label_18', 'Label_19', 'Label_20', 'Label_21', 'Label_22', 'Label_23', - # KEY_IMAGE], - # TYPE_TEXTBOX, self.get_Angle_sec_properties) - # change_tab.append(t2) - # - # t6 = (DISP_TITLE_ANGLE, [KEY_SECSIZE_SELECTED], [KEY_SOURCE], TYPE_TEXTBOX, self.change_source) - # change_tab.append(t6) - # - # t7 = (DISP_TITLE_CHANNEL, [KEY_SECSIZE_SELECTED], [KEY_SOURCE], TYPE_TEXTBOX, self.change_source) - # change_tab.append(t7) - # - return change_tab - - def edit_tabs(self): - """ This function is required if the tab name changes based on connectivity or profile or any other key. - Not required for this module but empty list should be passed""" - return [] - - def input_dictionary_design_pref(self): - """ - - :return: This function is used to choose values of design preferences to be saved to design dictionary. - - It returns list of tuple which contains, tab name, input widget type of keys, keys whose values to be saved, - - [(Tab Name, input widget type of keys, [List of keys to be saved])] - - """ - design_input = [] - - t2 = (KEY_DISP_COLSEC, TYPE_COMBOBOX, [KEY_SEC_MATERIAL]) - design_input.append(t2) - - t7 = ("Connector", TYPE_COMBOBOX, [KEY_CONNECTOR_MATERIAL]) - design_input.append(t7) - - return design_input - - def input_dictionary_without_design_pref(self): - """ - - :return: This function is used to choose values of design preferences to be saved to - design dictionary if design preference is never opened by user. It sets are design preference values to default. - If any design preference value needs to be set to input dock value, tuple shall be written as: - - (Key of input dock, [List of Keys from design prefernce], 'Input Dock') - - If the values needs to be set to default, - - (None, [List of Design Prefernce Keys], '') - - """ - design_input = [] - t1 = (KEY_MATERIAL, [KEY_SEC_MATERIAL], 'Input Dock') - design_input.append(t1) - - t2 = (None, [KEY_DP_DESIGN_METHOD], '') - design_input.append(t2) - - return design_input - - def refresh_input_dock(self): - """ - - :return: This function returns list of tuples which has keys that needs to be updated, - on changing Keys in design preference (ex: adding a new section to database should reflect in input dock) - - [(Tab Name, Input Dock Key, Input Dock Key type, design preference key, Master key, Value, Database Table Name)] - """ - add_buttons = [] - - t2 = (KEY_DISP_COLSEC, KEY_SECSIZE, TYPE_COMBOBOX, KEY_SECSIZE, None, None, "Columns") - add_buttons.append(t2) - - return add_buttons - - def get_values_for_design_pref(self, key, design_dictionary): - - if design_dictionary[KEY_MATERIAL] != 'Select Material': - fu = Material(design_dictionary[KEY_MATERIAL], 41).fu - else: - fu = '' - - val = {KEY_DP_DESIGN_METHOD: "Limit State Design" - }[key] - - return val - - #################################### - # Design Preference Functions End - #################################### - - def module_name(self): - return KEY_DISP_COMPRESSION - - def set_osdaglogger(key): - - """ - Function to set Logger for FinPlate Module - """ - - # @author Arsil Zunzunia - global logger - logger = logging.getLogger('osdag') - - logger.setLevel(logging.DEBUG) - handler = logging.StreamHandler() - # handler.setLevel(logging.DEBUG) - formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S') - - handler.setFormatter(formatter) - logger.addHandler(handler) - handler = logging.FileHandler('logging_text.log') - - # handler.setLevel(logging.DEBUG) - formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S') - handler.setFormatter(formatter) - logger.addHandler(handler) - # handler.setLevel(logging.INFO) - # formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S') - # handler.setFormatter(formatter) - # logger.addHandler(handler) - if key is not None: - handler = OurLog(key) - # handler.setLevel(logging.DEBUG) - formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%H:%M:%S') - handler.setFormatter(formatter) - logger.addHandler(handler) - - def customized_input(self): - - c_lst = [] - - t1 = (KEY_SECSIZE, self.fn_profile_section) - c_lst.append(t1) - - return c_lst - - def input_values(self): - - ''' - Fuction to return a list of tuples to be displayed as the UI.(Input Dock) - ''' - - # @author: Amir, Umair - - options_list = [] - - t1 = (KEY_MODULE, KEY_DISP_COMPRESSION, TYPE_MODULE, None, True, 'No Validator') - options_list.append(t1) - - t2 = (KEY_SEC_PROFILE, KEY_DISP_SEC_PROFILE, TYPE_COMBOBOX, VALUES_SEC_PROFILE, True, 'No Validator') - options_list.append(t2) - - t4 = (KEY_SECSIZE, KEY_DISP_SECSIZE, TYPE_COMBOBOX_CUSTOMIZED, ['All','Customized'], True, 'No Validator') - options_list.append(t4) - - t4 = (KEY_MATERIAL, KEY_DISP_MATERIAL, TYPE_COMBOBOX, VALUES_MATERIAL, True, 'No Validator') - options_list.append(t4) - - t5 = (KEY_LENZZ, KEY_DISP_LENZZ, TYPE_TEXTBOX, None, True, 'No Validator') - options_list.append(t5) - - t6 = (KEY_LENYY, KEY_DISP_LENYY, TYPE_TEXTBOX, None, True, 'No Validator') - options_list.append(t6) - - t7 = (None, DISP_TITLE_FSL, TYPE_TITLE, None, True, 'No Validator') - options_list.append(t7) - - t8 = (KEY_AXIAL, KEY_DISP_AXIAL, TYPE_TEXTBOX, None, True, 'No Validator') - options_list.append(t8) - - t12 = (KEY_MOMENT_MAJOR, KEY_DISP_MOMENT_MAJOR, TYPE_TEXTBOX, None, True, 'No Validator') - options_list.append(t12) - - t13 = (KEY_MOMENT_MINOR, KEY_DISP_MOMENT_MINOR, TYPE_TEXTBOX, None, True, 'No Validator') - options_list.append(t13) - - t9 = (None, DISP_TITLE_SC, TYPE_TITLE, None, True, 'No Validator') - options_list.append(t9) - - t10 = (KEY_END1, KEY_DISP_END1, TYPE_COMBOBOX, VALUES_END1, True, 'No Validator') - options_list.append(t10) - - t11 = (KEY_END2, KEY_DISP_END2, TYPE_COMBOBOX, VALUES_END2, True, 'No Validator') - options_list.append(t11) - - t12 = (KEY_IMAGE, None, TYPE_IMAGE_COMPRESSION, "./ResourceFiles/images/6.RRRR.PNG", True, 'No Validator') - options_list.append(t12) - - return options_list - - def fn_profile_section(self): - - profile = self[0] - if profile == 'Beams': - return connectdb("Beams", call_type="popup") - elif profile == 'Columns': - return connectdb("Columns", call_type="popup") - elif profile == 'RHS': - return connectdb("RHS", call_type="popup") - elif profile == 'SHS': - return connectdb("SHS", call_type="popup") - elif profile == 'CHS': - return connectdb("CHS", call_type="popup") - - - def fn_end1_end2(self): - - end1 = self[0] - if end1 == 'Fixed': - return VALUES_END2 - elif end1 == 'Free': - return ['Fixed'] - elif end1 == 'Hinged': - return ['Fixed', 'Hinged', 'Roller'] - elif end1 == 'Roller': - return ['Fixed', 'Hinged'] - - def fn_end1_image(self): - - if self == 'Fixed': - return "./ResourceFiles/images/6.RRRR.PNG" - elif self == 'Free': - return "./ResourceFiles/images/1.RRFF.PNG" - elif self == 'Hinged': - return "./ResourceFiles/images/5.RRRF.PNG" - elif self == 'Roller': - return "./ResourceFiles/images/4.RRFR.PNG" - - def fn_end2_image(self): - - end1 = self[0] - end2 = self[1] - - if end1 == 'Fixed': - if end2 == 'Fixed': - return "./ResourceFiles/images/6.RRRR.PNG" - elif end2 == 'Free': - return "./ResourceFiles/images/1.RRFF_rotated.PNG" - elif end2 == 'Hinged': - return "./ResourceFiles/images/5.RRRF_rotated.PNG" - elif end2 == 'Roller': - return "./ResourceFiles/images/4.RRFR_rotated.PNG" - elif end1 == 'Free': - return "./ResourceFiles/images/1.RRFF.PNG" - elif end1 == 'Hinged': - if end2 == 'Fixed': - return "./ResourceFiles/images/5.RRRF.PNG" - elif end2 == 'Hinged': - return "./ResourceFiles/images/3.RFRF.PNG" - elif end2 == 'Roller': - return "./ResourceFiles/images/2.FRFR_rotated.PNG" - elif end1 == 'Roller': - if end2 == 'Fixed': - return "./ResourceFiles/images/4.RRFR.PNG" - elif end2 == 'Hinged': - return "./ResourceFiles/images/2.FRFR.PNG" - - def input_value_changed(self): - - lst = [] - - t1 = ([KEY_SEC_PROFILE], KEY_SECSIZE, TYPE_COMBOBOX_CUSTOMIZED, self.fn_profile_section) - lst.append(t1) - - t2 = ([KEY_END1], KEY_END2, TYPE_COMBOBOX, self.fn_end1_end2) - lst.append(t2) - - t3 = ([KEY_END1, KEY_END2], KEY_IMAGE, TYPE_IMAGE, self.fn_end2_image) - lst.append(t3) - - # t4 = (KEY_END2, KEY_IMAGE, TYPE_IMAGE, self.fn_end2_image) - # lst.append(t4) - - return lst - - def output_values(self,flag): - out_list = [] - t1 = (None, DISP_TITLE_TENSION_SECTION, TYPE_TITLE, None, True) - out_list.append(t1) - - return out_list - def func_for_validation(self, design_dictionary): - - all_errors = [] - self.design_status = False - flag = False - option_list = self.input_values(self) - missing_fields_list = [] - for option in option_list: - if option[2] == TYPE_TEXTBOX: - if design_dictionary[option[0]] == '': - missing_fields_list.append(option[1]) - elif option[2] == TYPE_COMBOBOX and option[0] not in [KEY_SEC_PROFILE, KEY_END1, KEY_END2]: - val = option[3] - if design_dictionary[option[0]] == val[0]: - missing_fields_list.append(option[1]) - - if len(missing_fields_list) > 0: - - error = self.generate_missing_fields_error_string(self,missing_fields_list) - all_errors.append(error) - # flag = False - else: - flag = True - - if flag: - self.set_input_values(self, design_dictionary) - # print(design_dictionary) - else: - return all_errors - - def set_input_values(self, design_dictionary): - self.module = design_dictionary[KEY_MODULE] - self.sizelist = design_dictionary[KEY_SECSIZE] - self.sec_profile = design_dictionary[KEY_SEC_PROFILE] - self.material = design_dictionary[KEY_SEC_MATERIAL] - self.length_zz = float(design_dictionary[KEY_LENZZ]) - self.length_yy = float(design_dictionary[KEY_LENYY]) - self.load = Load(shear_force="", axial_force=design_dictionary[KEY_AXIAL],moment=design_dictionary[KEY_MOMENT_MAJOR], - moment_minor = design_dictionary[KEY_MOMENT_MINOR],unit_kNm=True) - - print(self.module) - print(self.sec_profile) - print(self.material) - print(self.length_yy) - print(self.length_zz) - print(self.load) - # Assuming first member as selected size - selectedsize = design_dictionary[KEY_SECSIZE][0] - self.select_section(self,selectedsize) - - def select_section(self, selectedsize): - - "selecting components class based on the section passed " - - if self.sec_profile == 'RHS': - self.section_size = RHS(designation=selectedsize, material_grade=self.material) - elif self.sec_profile == 'CHS': - self.section_size = CHS(designation=selectedsize, material_grade=self.material) - elif self.sec_profile == 'SHS': - self.section_size = SHS(designation=selectedsize, material_grade=self.material) - elif self.sec_profile in ['Beams','Columns']: - self.section_size = ISection(designation=selectedsize, material_grade=self.material) - else: - pass - - print(self.section_size) - - def get_3d_components(self): - - components = [] - return components diff --git a/design_type/connection/cleat_angle_connection.py b/design_type/connection/cleat_angle_connection.py deleted file mode 100644 index afa5bab38..000000000 --- a/design_type/connection/cleat_angle_connection.py +++ /dev/null @@ -1 +0,0 @@ -from design_type.connection.shear_connection import ShearConnection from utils.common.component import * from utils.common.component import Bolt, Plate, Weld from Common import * import sys from Report_functions import * from design_report.reportGenerator_latex import CreateLatex from utils.common.load import Load import logging class CleatAngleConnection(ShearConnection): def __init__(self): super(CleatAngleConnection, self).__init__() self.sptd_leg_length = 0.0 self.sptIng_leg_length = 0.0 self.design_status = False ############################################### # Design Preference Functions Start ############################################### def tab_list(self): tabs = [] t1 = (KEY_DISP_COLSEC, TYPE_TAB_1, self.tab_supporting_section) tabs.append(t1) t1 = (KEY_DISP_BEAMSEC, TYPE_TAB_1, self.tab_supported_section) tabs.append(t1) t6 = (DISP_TITLE_CLEAT, TYPE_TAB_1, self.tab_angle_section) tabs.append(t6) t2 = ("Bolt", TYPE_TAB_2, self.bolt_values) tabs.append(t2) t4 = ("Detailing", TYPE_TAB_2, self.detailing_values) tabs.append(t4) t5 = ("Design", TYPE_TAB_2, self.design_values) tabs.append(t5) return tabs def tab_value_changed(self): change_tab = [] t1 = (KEY_DISP_COLSEC, [KEY_SUPTNGSEC_MATERIAL], [KEY_SUPTNGSEC_FU, KEY_SUPTNGSEC_FY], TYPE_TEXTBOX, self.get_fu_fy_I_section_suptng) change_tab.append(t1) t2 = (KEY_DISP_BEAMSEC, [KEY_SUPTDSEC_MATERIAL], [KEY_SUPTDSEC_FU, KEY_SUPTDSEC_FY], TYPE_TEXTBOX, self.get_fu_fy_I_section_suptd) change_tab.append(t2) t5 = (DISP_TITLE_CLEAT, ['Label_1', 'Label_2','Label_3'], ['Label_7', 'Label_8', 'Label_9', 'Label_10', 'Label_11', 'Label_12', 'Label_13', 'Label_14', 'Label_15', 'Label_16', 'Label_17', 'Label_18', 'Label_19', 'Label_20', 'Label_21', 'Label_22', 'Label_23', KEY_IMAGE], TYPE_TEXTBOX, self.get_Angle_sec_properties) change_tab.append(t5) t6 = (DISP_TITLE_CLEAT, [KEY_ANGLE_LIST, KEY_CONNECTOR_MATERIAL], [KEY_ANGLE_SELECTED, KEY_CONNECTOR_FY, KEY_CONNECTOR_FU, 'Label_1', 'Label_2', 'Label_3', 'Label_4', 'Label_5', 'Label_7', 'Label_8', 'Label_9','Label_10', 'Label_11', 'Label_12', 'Label_13', 'Label_14', 'Label_15', 'Label_16', 'Label_17', 'Label_18','Label_19', 'Label_20', 'Label_21', 'Label_22', 'Label_23','Label_24', KEY_IMAGE], TYPE_TEXTBOX, self.get_new_angle_section_properties) change_tab.append(t6) t4 = (KEY_DISP_COLSEC, ['Label_1', 'Label_2', 'Label_3', 'Label_4', 'Label_5'], ['Label_11', 'Label_12', 'Label_13', 'Label_14', 'Label_15', 'Label_16', 'Label_17', 'Label_18', 'Label_19', 'Label_20','Label_21','Label_22',KEY_IMAGE], TYPE_TEXTBOX, self.get_I_sec_properties) change_tab.append(t4) t5 = (KEY_DISP_BEAMSEC, ['Label_1', 'Label_2', 'Label_3', 'Label_4', 'Label_5'], ['Label_11', 'Label_12', 'Label_13', 'Label_14', 'Label_15', 'Label_16', 'Label_17', 'Label_18', 'Label_19', 'Label_20','Label_21','Label_22',KEY_IMAGE], TYPE_TEXTBOX, self.get_I_sec_properties) change_tab.append(t5) t6 = (KEY_DISP_COLSEC, [KEY_SUPTNGSEC], [KEY_SOURCE], TYPE_TEXTBOX, self.change_source) change_tab.append(t6) t7 = (KEY_DISP_BEAMSEC, [KEY_SUPTDSEC], [KEY_SOURCE], TYPE_TEXTBOX, self.change_source) change_tab.append(t7) t8 = (DISP_TITLE_CLEAT, [KEY_ANGLE_SELECTED], [KEY_SOURCE], TYPE_TEXTBOX, self.change_source) change_tab.append(t8) return change_tab def input_dictionary_design_pref(self): design_input = [] t1 = (KEY_DISP_COLSEC, TYPE_COMBOBOX, [KEY_SUPTNGSEC_MATERIAL]) design_input.append(t1) # t1 = (KEY_DISP_COLSEC, TYPE_TEXTBOX, [KEY_SUPTNGSEC_FU, KEY_SUPTNGSEC_FY]) # design_input.append(t1) t2 = (KEY_DISP_BEAMSEC, TYPE_COMBOBOX, [KEY_SUPTDSEC_MATERIAL]) design_input.append(t2) # t2 = (KEY_DISP_BEAMSEC, TYPE_TEXTBOX, [KEY_SUPTDSEC_FU, KEY_SUPTDSEC_FY]) # design_input.append(t2) t2 = (DISP_TITLE_CLEAT, TYPE_COMBOBOX, [KEY_CONNECTOR_MATERIAL]) design_input.append(t2) t3 = ("Bolt", TYPE_COMBOBOX, [KEY_DP_BOLT_TYPE, KEY_DP_BOLT_HOLE_TYPE, KEY_DP_BOLT_SLIP_FACTOR]) design_input.append(t3) t5 = ("Detailing", TYPE_COMBOBOX, [KEY_DP_DETAILING_EDGE_TYPE, KEY_DP_DETAILING_CORROSIVE_INFLUENCES]) design_input.append(t5) t5 = ("Detailing", TYPE_TEXTBOX, [KEY_DP_DETAILING_GAP]) design_input.append(t5) t6 = ("Design", TYPE_COMBOBOX, [KEY_DP_DESIGN_METHOD]) design_input.append(t6) return design_input def input_dictionary_without_design_pref(self): design_input = [] t1 = (KEY_MATERIAL, [KEY_SUPTNGSEC_MATERIAL, KEY_SUPTDSEC_MATERIAL], 'Input Dock') design_input.append(t1) t2 = (None, [KEY_DP_BOLT_TYPE, KEY_DP_BOLT_HOLE_TYPE, KEY_DP_BOLT_SLIP_FACTOR, KEY_DP_DETAILING_EDGE_TYPE, KEY_DP_DETAILING_GAP, KEY_DP_DETAILING_CORROSIVE_INFLUENCES, KEY_DP_DESIGN_METHOD, KEY_CONNECTOR_MATERIAL], '') design_input.append(t2) return design_input def refresh_input_dock(self): """ :return: This function returns list of tuples which has keys that needs to be updated, on changing Keys in design preference (ex: adding a new section to database should reflect in input dock) [(Tab Name, Input Dock Key, Input Dock Key type, design preference key, Master key, Value, Database Table Name)] """ add_buttons = [] t1 = (KEY_DISP_COLSEC, KEY_SUPTNGSEC, TYPE_COMBOBOX, KEY_SUPTNGSEC, KEY_CONN, VALUES_CONN_1, "Columns") add_buttons.append(t1) t1 = (KEY_DISP_COLSEC, KEY_SUPTNGSEC, TYPE_COMBOBOX, KEY_SUPTNGSEC, KEY_CONN, VALUES_CONN_2, "Beams") add_buttons.append(t1) t2 = (KEY_DISP_BEAMSEC, KEY_SUPTDSEC, TYPE_COMBOBOX, KEY_SUPTDSEC, None, None, "Beams") add_buttons.append(t2) t2 = (DISP_TITLE_CLEAT, KEY_ANGLE_LIST, TYPE_COMBOBOX_CUSTOMIZED, KEY_ANGLE_SELECTED, None, None, "Angles") add_buttons.append(t2) return add_buttons #################################### # Design Preference Functions End #################################### def input_values(self): self.module = KEY_DISP_CLEATANGLE options_list = [] t1 = (None, DISP_TITLE_CM, TYPE_TITLE, None, True, 'No Validator') options_list.append(t1) t2 = (KEY_CONN, KEY_DISP_CONN, TYPE_COMBOBOX, VALUES_CONN, True, 'No Validator') options_list.append(t2) t3 = (KEY_IMAGE, None, TYPE_IMAGE, './ResourceFiles/images/fin_cf_bw.png', True, 'No Validator') options_list.append(t3) t4 = (KEY_SUPTNGSEC, KEY_DISP_COLSEC, TYPE_COMBOBOX, VALUES_COLSEC, True, 'No Validator') options_list.append(t4) t5 = (KEY_SUPTDSEC, KEY_DISP_BEAMSEC, TYPE_COMBOBOX, VALUES_BEAMSEC, True, 'No Validator') options_list.append(t5) t6 = (KEY_MATERIAL, KEY_DISP_MATERIAL, TYPE_COMBOBOX, VALUES_MATERIAL, True, 'No Validator') options_list.append(t6) t7 = (None, DISP_TITLE_FSL, TYPE_TITLE, None, True, 'No Validator') options_list.append(t7) t8 = (KEY_SHEAR, KEY_DISP_SHEAR, TYPE_TEXTBOX, None, True, 'No Validator') options_list.append(t8) t9 = (None, DISP_TITLE_BOLT, TYPE_TITLE, None, True, 'No Validator') options_list.append(t9) t10 = (KEY_D, KEY_DISP_D, TYPE_COMBOBOX_CUSTOMIZED, VALUES_D, True, 'No Validator') options_list.append(t10) t11 = (KEY_TYP, KEY_DISP_TYP, TYPE_COMBOBOX, VALUES_TYP, True, 'No Validator') options_list.append(t11) t12 = (KEY_GRD, KEY_DISP_GRD, TYPE_COMBOBOX_CUSTOMIZED, VALUES_GRD, True, 'No Validator') options_list.append(t12) t13 = (None, DISP_TITLE_CLEAT, TYPE_TITLE, None, True, 'No Validator') options_list.append(t13) t15 = (KEY_ANGLE_LIST, KEY_DISP_CLEATSEC, TYPE_COMBOBOX_CUSTOMIZED, VALUES_ANGLESEC, True, 'No Validator') options_list.append(t15) t16 = (KEY_MODULE, KEY_DISP_CLEATANGLE, TYPE_MODULE, None, True, 'No Validator') options_list.append(t16) return options_list @staticmethod def cleatsec_customized(): a = VALUES_CLEAT_CUSTOMIZED return a # @staticmethod # def diam_bolt_customized(): # c = connectdb1() # if "36" in c: c.remove("36") # return c def customized_input(self): list1 = [] t1 = (KEY_GRD, self.grdval_customized) list1.append(t1) t2 = (KEY_ANGLE_LIST, self.cleatsec_customized) list1.append(t2) t3 = (KEY_D, self.diam_bolt_customized) list1.append(t3) return list1 def fn_conn_suptngsec_lbl(self): conn = self[0] if conn in VALUES_CONN_1: return KEY_DISP_COLSEC elif conn in VALUES_CONN_2: return KEY_DISP_PRIBM else: return '' def fn_conn_suptdsec_lbl(self): conn = self[0] if conn in VALUES_CONN_1: return KEY_DISP_BEAMSEC elif conn in VALUES_CONN_2: return KEY_DISP_SECBM else: return '' def fn_conn_suptngsec(self): conn = self[0] if conn in VALUES_CONN_1: return VALUES_COLSEC elif conn in VALUES_CONN_2: return VALUES_PRIBM else: return [] def fn_conn_suptdsec(self): conn = self[0] if conn in VALUES_CONN_1: return VALUES_BEAMSEC elif conn in VALUES_CONN_2: return VALUES_SECBM else: return [] def fn_conn_image(self): conn = self[0] if conn == VALUES_CONN[0]: return './ResourceFiles/images/fin_cf_bw.png' elif conn == VALUES_CONN[1]: return './ResourceFiles/images/fin_cw_bw.png' elif conn in VALUES_CONN_2: return './ResourceFiles/images/fin_beam_beam.png' else: return '' def input_value_changed(self): lst = [] t1 = ([KEY_CONN], KEY_SUPTNGSEC, TYPE_LABEL, self.fn_conn_suptngsec_lbl) lst.append(t1) t2 = ([KEY_CONN], KEY_SUPTNGSEC, TYPE_COMBOBOX, self.fn_conn_suptngsec) lst.append(t2) t3 = ([KEY_CONN], KEY_SUPTDSEC, TYPE_LABEL, self.fn_conn_suptdsec_lbl) lst.append(t3) t4 = ([KEY_CONN], KEY_SUPTDSEC, TYPE_COMBOBOX, self.fn_conn_suptdsec) lst.append(t4) t5 = ([KEY_CONN], KEY_IMAGE, TYPE_IMAGE, self.fn_conn_image) lst.append(t5) t6 = ([KEY_MATERIAL], KEY_MATERIAL, TYPE_CUSTOM_MATERIAL, self.new_material) lst.append(t6) return lst def get_3d_components(self): components = [] t1 = ('Model', self.call_3DModel) components.append(t1) t2 = ('Beam', self.call_3DBeam) components.append(t2) t3 = ('Column', self.call_3DColumn) components.append(t3) t4 = ('Cleat Angle', self.call_3DCleat) components.append(t4) return components def call_3DCleat(self, ui, bgcolor): from PyQt5.QtWidgets import QCheckBox from PyQt5.QtCore import Qt for chkbox in ui.frame.children(): if chkbox.objectName() == 'Cleat Angle': continue if isinstance(chkbox, QCheckBox): chkbox.setChecked(Qt.Unchecked) ui.commLogicObj.display_3DModel("cleatAngle", bgcolor) def output_values(self, flag): """ Function to return a list of tuples to be displayed as the UI.(Output Dock) """ # @author: Umair out_list = [] """""""""""""""""""""""""""""""""""""""""""""""""""""" """ Cleat Angle Properties: Start """ t20 = (None, DISP_OUT_TITLE_CLEAT, TYPE_TITLE, None, True) out_list.append(t20) t21 = (KEY_OUT_CLEAT_SECTION, KEY_OUT_DISP_CLEAT_SECTION, TYPE_TEXTBOX, self.cleat.designation if flag else '', True) out_list.append(t21) t15 = (KEY_OUT_CLEAT_HEIGHT, KEY_OUT_DISP_CLEAT_HEIGHT, TYPE_TEXTBOX, self.sptd_leg.height if flag else '', True) out_list.append(t15) t17 = (KEY_OUT_CLEAT_SHEAR, KEY_DISP_SHEAR_YLD, TYPE_TEXTBOX, round(self.sptd_leg.cleat_shear_capacity / 1000, 2) if flag else '', True) out_list.append(t17) t18 = (KEY_OUT_CLEAT_BLK_SHEAR, KEY_DISP_BLK_SHEAR, TYPE_TEXTBOX, round(self.sptd_leg.block_shear_capacity / 1000, 2) if flag else '', True) out_list.append(t18) t19 = (KEY_OUT_CLEAT_MOM_DEMAND, KEY_DISP_MOM_DEMAND, TYPE_TEXTBOX, round(self.sptd_leg.moment_demand / 1000000, 2) if flag else '', True) out_list.append(t19) # t20 = (KEY_OUT_CLEAT_MOM_CAPACITY, KEY_DISP_MOM_CAPACITY, TYPE_TEXTBOX, round(self.sptd_leg.cleat_moment_capacity / 1000000, 2) if flag else '', True) out_list.append(t20) """ Cleat Angle Properties: End """ """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """ Bolt Properties: Start """ t1 = (None, DISP_TITLE_BOLT, TYPE_TITLE, None, True) out_list.append(t1) t2 = (KEY_OUT_D_PROVIDED, KEY_OUT_DISP_D_PROVIDED, TYPE_TEXTBOX, self.bolt.bolt_diameter_provided if flag else '', True) out_list.append(t2) t3 = (KEY_OUT_GRD_PROVIDED, KEY_OUT_DISP_PC_PROVIDED, TYPE_TEXTBOX, self.bolt.bolt_PC_provided if flag else '', True) out_list.append(t3) """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """ Bolt Properties- Supported leg: Start """ t4 = (None, DISP_OUT_TITLE_SPTDLEG, TYPE_TITLE, None, True) out_list.append(t4) t9 = (KEY_OUT_BOLT_LINE, KEY_OUT_DISP_BOLT_LINE, TYPE_TEXTBOX, self.sptd_leg.bolt_line if flag else '', True) out_list.append(t9) t10 = (KEY_OUT_BOLTS_ONE_LINE, KEY_OUT_DISP_BOLTS_ONE_LINE, TYPE_TEXTBOX, self.sptd_leg.bolts_one_line if flag else '', True) out_list.append(t10) t8 = (KEY_OUT_BOLT_FORCE, KEY_OUT_DISP_BOLT_FORCE, TYPE_TEXTBOX, round(self.sptd_leg.bolt_force / 1000, 2) if flag else '', True) out_list.append(t8) t6 = (KEY_OUT_BOLT_CAPACITY_SPTD, KEY_OUT_DISP_BOLT_VALUE, TYPE_TEXTBOX, self.bolt_capacity_disp_sptd if flag else '', True) out_list.append(t6) t3_2 = (KEY_OUT_BOLT_IR_DETAILS_SPTD, KEY_OUT_DISP_BOLT_IR_DETAILS, TYPE_OUT_BUTTON, ['Details', self.bolt_capacity_details_supported], True) out_list.append(t3_2) t11 = (KEY_OUT_SPACING, KEY_OUT_DISP_SPACING, TYPE_OUT_BUTTON, ['Spacing Details', self.spacing], True) out_list.append(t11) """ Bolt Properties- Supported leg: End """ """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """ Bolt Properties- Supporting leg: Start """ t12 = (None, DISP_OUT_TITLE_SPTINGLEG, TYPE_TITLE, None, True) out_list.append(t12) t17 = (KEY_OUT_SPTING_BOLT_LINE, KEY_OUT_DISP_BOLT_LINE, TYPE_TEXTBOX, self.spting_leg.bolt_line if flag else '', True) out_list.append(t17) t18 = (KEY_OUT_SPTING_BOLTS_ONE_LINE, KEY_OUT_DISP_BOLTS_ONE_LINE, TYPE_TEXTBOX, self.spting_leg.bolts_one_line if flag else '', True) out_list.append(t18) t16 = (KEY_OUT_SPTING_BOLT_FORCE, KEY_OUT_DISP_BOLT_FORCE, TYPE_TEXTBOX, round(self.spting_leg.bolt_force / 1000, 2) if flag else '', True) out_list.append(t16) t6 = (KEY_OUT_BOLT_CAPACITY_SPTING, KEY_OUT_DISP_BOLT_VALUE, TYPE_TEXTBOX, self.bolt_capacity_disp_spting if flag else '', True) out_list.append(t6) t3_2 = (KEY_OUT_BOLT_IR_DETAILS_SPTING, KEY_OUT_DISP_BOLT_IR_DETAILS, TYPE_OUT_BUTTON, ['Details', self.bolt_capacity_details_suporting], True) out_list.append(t3_2) t19 = (KEY_OUT_SPTING_SPACING, KEY_OUT_DISP_SPACING, TYPE_OUT_BUTTON, ['Spacing Details', self.spting_spacing], True) out_list.append(t19) """ Bolt Properties- Supporting leg: End """ """""""""""""""""""""""""""""""""""""""""""""""""""""""" return out_list def bolt_capacity_details_supported(self, flag): bolt_details_sptd = [] t4 = (KEY_OUT_BOLT_SHEAR, KEY_OUT_DISP_BOLT_SHEAR, TYPE_TEXTBOX, round(self.bolt.bolt_shear_capacity/1000,2) if flag else '', True) bolt_details_sptd.append(t4) bolt_bearing_capacity_disp = '' if flag is True: print("wats this",self.bolt.bolt_bearing_capacity) if self.bolt.bolt_bearing_capacity != 'N/A': bolt_bearing_capacity_disp = round(self.bolt.bolt_bearing_capacity / 1000, 2) else: bolt_bearing_capacity_disp = self.bolt.bolt_bearing_capacity t5 = (KEY_OUT_BOLT_BEARING, KEY_OUT_DISP_BOLT_BEARING, TYPE_TEXTBOX, bolt_bearing_capacity_disp if flag else '', True) bolt_details_sptd.append(t5) t5_1 = (KEY_OUT_BETA_LJ, KEY_OUT_DISP_BETA_LJ, TYPE_TEXTBOX, round(self.beta_lj_sptd, 3) if flag else 'N/A', True) bolt_details_sptd.append(t5_1) t5_2 = (KEY_OUT_BETA_LG, KEY_OUT_DISP_BETA_LG, TYPE_TEXTBOX, round(self.beta_lg_sptd, 3) if flag and self.bolt.bolt_type == TYP_BEARING else 'N/A', True) bolt_details_sptd.append(t5_2) t6 = (KEY_OUT_BOLT_CAPACITY, KEY_OUT_DISP_BOLT_VALUE, TYPE_TEXTBOX, self.bolt_capacity_disp_sptd if flag else '', True) bolt_details_sptd.append(t6) t21 = (KEY_OUT_BOLT_FORCE, KEY_OUT_DISP_BOLT_SHEAR_FORCE, TYPE_TEXTBOX, round(self.sptd_leg.bolt_force / 1000, 2) if flag else '', True) bolt_details_sptd.append(t21) return bolt_details_sptd def bolt_capacity_details_suporting(self, flag): bolt_details_spting = [] t4 = (KEY_OUT_BOLT_SHEAR, KEY_OUT_DISP_BOLT_SHEAR, TYPE_TEXTBOX, round(self.bolt2.bolt_shear_capacity / 1000, 2) if flag else '', True) bolt_details_spting.append(t4) bolt_bearing_capacity_disp = '' if flag is True: if self.bolt.bolt_bearing_capacity != 'N/A': bolt_bearing_capacity_disp = round(self.bolt2.bolt_bearing_capacity / 1000, 2) else: bolt_bearing_capacity_disp = self.bolt2.bolt_bearing_capacity t5 = (KEY_OUT_BOLT_BEARING, KEY_OUT_DISP_BOLT_BEARING, TYPE_TEXTBOX, bolt_bearing_capacity_disp if flag else '', True) bolt_details_spting.append(t5) t5_1 = (KEY_OUT_BETA_LJ, KEY_OUT_DISP_BETA_LJ, TYPE_TEXTBOX, round(self.beta_lj_spting, 3) if flag else 'N/A', True) bolt_details_spting.append(t5_1) t5_2 = (KEY_OUT_BETA_LG, KEY_OUT_DISP_BETA_LG, TYPE_TEXTBOX, round(self.beta_lg_spting, 3) if flag and self.bolt.bolt_type == TYP_BEARING else 'N/A', True) bolt_details_spting.append(t5_2) t6 = (KEY_OUT_BOLT_CAPACITY, KEY_OUT_DISP_BOLT_VALUE, TYPE_TEXTBOX, self.bolt_capacity_disp_spting if flag else '', True) bolt_details_spting.append(t6) t21 = (KEY_OUT_BOLT_FORCE, KEY_OUT_DISP_BOLT_SHEAR_FORCE, TYPE_TEXTBOX, round(self.spting_leg.bolt_force / 1000, 2) if flag else '', True) bolt_details_spting.append(t21) return bolt_details_spting def spacing(self, status): spacing = [] t00 = (None, "", TYPE_NOTE, "Representative Image for Spacing Details") spacing.append(t00) t99 = (None, 'Spacing Details', TYPE_SECTION, ['./ResourceFiles/images/cleat_beam.png', 400, 277, ""]) # [image, width, height, caption] spacing.append(t99) t9 = (KEY_OUT_PITCH, KEY_OUT_DISP_PITCH, TYPE_TEXTBOX, self.sptd_leg.gauge_provided if status else '') spacing.append(t9) t10 = (KEY_OUT_END_DIST, KEY_OUT_DISP_END_DIST, TYPE_TEXTBOX, self.sptd_leg.edge_dist_provided if status else '') spacing.append(t10) gauge1 = (max(self.cleat.thickness + self.cleat.root_radius, self.sptd_leg.gap) + self.sptd_leg.end_dist_provided) t11 = (KEY_OUT_GAUGE1, KEY_OUT_DISP_GAUGE1, TYPE_TEXTBOX, gauge1 if status else '') spacing.append(t11) t11 = (KEY_OUT_GAUGE2, KEY_OUT_DISP_GAUGE2, TYPE_TEXTBOX, self.sptd_leg.pitch_provided if status else '') spacing.append(t11) edge = (self.cleat.leg_a_length - self.sptd_leg.pitch_provided * (self.sptd_leg.bolt_line - 1) - gauge1) t12 = (KEY_OUT_EDGE_DIST, KEY_OUT_DISP_EDGE_DIST, TYPE_TEXTBOX, edge if status else '') spacing.append(t12) return spacing def spting_spacing(self, status): spting_spacing = [] t00 = (None, "", TYPE_NOTE, "Representative Image for Spacing Details") spting_spacing.append(t00) t99 = (None, 'Spacing Details', TYPE_SECTION, ['./ResourceFiles/images/cleat.png', 400, 277, ""]) # [image, width, height, caption] spting_spacing.append(t99) t9 = (KEY_OUT_PITCH, KEY_OUT_DISP_PITCH, TYPE_TEXTBOX, self.spting_leg.gauge_provided if status else '') spting_spacing.append(t9) t10 = (KEY_OUT_END_DIST, KEY_OUT_DISP_END_DIST, TYPE_TEXTBOX, self.spting_leg.edge_dist_provided if status else '') spting_spacing.append(t10) gauge1 = (self.cleat.thickness + self.cleat.root_radius + self.spting_leg.end_dist_provided) t11 = (KEY_OUT_GAUGE1, KEY_OUT_DISP_GAUGE1, TYPE_TEXTBOX, gauge1 if status else '') spting_spacing.append(t11) t11 = (KEY_OUT_GAUGE2, KEY_OUT_DISP_GAUGE2, TYPE_TEXTBOX, self.spting_leg.pitch_provided if status else '') spting_spacing.append(t11) edge = (self.cleat.leg_a_length - self.spting_leg.pitch_provided * (self.spting_leg.bolt_line - 1) - gauge1) t12 = (KEY_OUT_EDGE_DIST, KEY_OUT_DISP_EDGE_DIST, TYPE_TEXTBOX, edge if status else '') spting_spacing.append(t12) return spting_spacing def set_osdaglogger(key): """ Function to set Logger for FinPlate Module """ # @author Arsil Zunzunia global logger logger = logging.getLogger('Osdag') logger.setLevel(logging.DEBUG) handler = logging.StreamHandler() formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') handler.setFormatter(formatter) logger.addHandler(handler) handler = logging.FileHandler('logging_text.log') formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') handler.setFormatter(formatter) logger.addHandler(handler) if key is not None: handler = OurLog(key) formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') handler.setFormatter(formatter) logger.addHandler(handler) def module_name(self): return KEY_DISP_CLEATANGLE def set_input_values(self, design_dictionary): print(design_dictionary) super(CleatAngleConnection,self).set_input_values(self, design_dictionary) self.module = design_dictionary[KEY_MODULE] self.cleat_list = design_dictionary[KEY_ANGLE_LIST] self.cleat_material_grade = design_dictionary[KEY_CONNECTOR_MATERIAL] print(self.cleat_list) self.bolt2 = Bolt(grade=design_dictionary[KEY_GRD], diameter=design_dictionary[KEY_D], bolt_type=design_dictionary[KEY_TYP], bolt_hole_type=design_dictionary[KEY_DP_BOLT_HOLE_TYPE], edge_type=design_dictionary[KEY_DP_DETAILING_EDGE_TYPE], mu_f=design_dictionary.get(KEY_DP_BOLT_SLIP_FACTOR, None), corrosive_influences=design_dictionary[KEY_DP_DETAILING_CORROSIVE_INFLUENCES], bolt_tensioning=design_dictionary[KEY_DP_BOLT_TYPE]) self.sptd_leg = Plate(material_grade=design_dictionary[KEY_CONNECTOR_MATERIAL],gap=design_dictionary[KEY_DP_DETAILING_GAP]) self.spting_leg = Plate(material_grade=design_dictionary[KEY_CONNECTOR_MATERIAL],gap=design_dictionary[KEY_DP_DETAILING_GAP]) # logger.info("Input values are set. Checking if angle of required thickness is available") self.check_available_cleat_thk(self) def check_available_cleat_thk(self): self.thickness_list = [] self.cleat_list_thk = [] self.leg_lengths = [] min_thickness = self.supported_section.web_thickness / 2 if self.connectivity == VALUES_CONN_1[0]: self.available_length = (self.supporting_section.flange_width - self.supported_section.web_thickness )/2 elif self.connectivity == VALUES_CONN_1[1]: self.available_length = (self.supporting_section.depth - 2 * self.supporting_section.flange_thickness - 2 * self.supporting_section.root_radius - self.supported_section.web_thickness) / 2 else: self.available_length = math.inf for designation in self.cleat_list: cleat = Angle(designation=designation, material_grade=self.cleat_material_grade) if cleat.thickness*2 >= self.supported_section.web_thickness and cleat.leg_a_length <= self.available_length: self.cleat_list_thk.append(designation) print("added", designation) print(self.cleat_list_thk) else: if cleat.thickness not in self.thickness_list: self.thickness_list.append(cleat.thickness) print("added", designation, self.thickness_list) if cleat.leg_a_length not in self.leg_lengths: self.leg_lengths.append(cleat.leg_a_length) print("added", designation, self.leg_lengths) # self.cleat_list_leg = [] if self.cleat_list_thk: # logger.info("Required cleat thickness available. Doing preliminary member checks") self.member_capacity(self) else: if self.connectivity in VALUES_CONN_1: logger.error("Cleat Angle should have minimum thickness of {} and maximum leg length of {}." .format(min_thickness,round(self.available_length,2))) else: logger.error( "Cleat Angle should have minimum thickness of %2.2f." % min_thickness) def member_capacity(self): super(CleatAngleConnection, self).member_capacity(self) self.supported_section.low_shear_capacity = round(0.6 * self.supported_section.shear_yielding_capacity, 2) if self.supported_section.low_shear_capacity / 1000 > self.load.shear_force and \ self.supporting_section.tension_yielding_capacity / 1000 > self.load.axial_force: if self.load.shear_force <= min(round(0.15 * self.supported_section.shear_yielding_capacity / 1000, 0), 40.0): logger.warning(" : User input for shear force is very less compared to section capacity. " "Setting Shear Force value to 15% of supported beam shear capacity or 40kN, whichever is less.") self.load.shear_force = min(round(0.15 * self.supported_section.shear_yielding_capacity / 1000, 0), 40.0) print("preliminary member check is satisfactory. Checking available Bolt Diameters") self.supported_section.design_status = True self.select_bolt_dia_beam(self) else: self.design_status = False logger.warning( " : The shear yielding capacity (low shear case) {} and/or tension yielding capacity {} is less " "than the applied load. Define a large/larger section(s) or decrease the load." .format(round(self.supported_section.low_shear_capacity / 1000, 2), round(self.supported_section.tension_yielding_capacity / 1000, 2))) print( "The preliminary member check(s) have failed. Select a large/larger section(s) or decrease load and re-design.") def select_bolt_dia_beam(self): self.output = [] self.beta_lj_sptd = 1.0 self.beta_lg_sptd = 1.0 trial = 0 self.min_plate_height = self.supported_section.min_plate_height() print(self.min_plate_height, "is min height") self.max_plate_height = self.supported_section.max_plate_height(self.connectivity, self.supported_section.notch_ht) for self.cleatangle in self.cleat_list_thk: self.cleat = Angle(designation=self.cleatangle, material_grade=self.cleat_material_grade) # self.sptd_leg.thickness_provided = self.cleat.thickness bolts_required_previous = 2 self.bolt.bolt_PC_provided = self.bolt.bolt_grade[-1] count = 0 self.sptd_bolt_conn_plates_t_fu_fy = [] self.sptd_bolt_conn_plates_t_fu_fy.append((2*self.cleat.thickness, self.sptd_leg.fu, self.sptd_leg.fy)) self.sptd_bolt_conn_plates_t_fu_fy.append((self.supported_section.web_thickness, self.supported_section.fu, self.supported_section.fy)) """ # while considering eccentricity, distance from bolt line to supporting member will be, # end_dist+gap or end_dist+root_radius+cleat_thickness, whichever is maximum # """ self.end_to_sptd = max(self.sptd_leg.gap, self.cleat.thickness + self.cleat.root_radius) for self.bolt.bolt_diameter_provided in reversed(self.bolt.bolt_diameter): self.bolt.calculate_bolt_spacing_limits(bolt_diameter_provided=self.bolt.bolt_diameter_provided, conn_plates_t_fu_fy=self.sptd_bolt_conn_plates_t_fu_fy,n=2) self.bolt.calculate_bolt_capacity(bolt_diameter_provided=self.bolt.bolt_diameter_provided, bolt_grade_provided=self.bolt.bolt_PC_provided, conn_plates_t_fu_fy=self.sptd_bolt_conn_plates_t_fu_fy, n_planes=2) print("Suptd bolt capacity: ", self.bolt.bolt_capacity) self.l_j_sptd = self.sptd_leg.gauge_provided * (self.sptd_leg.bolts_one_line - 1) self.t_sum_sptd = self.supported_section.web_thickness + 2 * self.cleat.thickness self.beta_lj_sptd = IS800_2007.cl_10_3_3_1_bolt_long_joint(self.bolt.bolt_diameter_provided, self.l_j_sptd) if self.bolt.bolt_type == TYP_BEARING: self.beta_lg_sptd = IS800_2007.cl_10_3_3_2_bolt_large_grip(self.bolt.bolt_diameter_provided, self.t_sum_sptd, self.l_j_sptd) else: self.beta_lg_sptd = 1.0 print(self.min_plate_height,"is another min_height") self.sptd_leg.get_web_plate_details(bolt_dia=self.bolt.bolt_diameter_provided, web_plate_h_min=self.min_plate_height, web_plate_h_max=self.max_plate_height, bolt_capacity=self.bolt.bolt_capacity, min_edge_dist=self.bolt.min_edge_dist_round, min_gauge=self.bolt.min_gauge_round, max_spacing=self.bolt.max_spacing_round, max_edge_dist=self.bolt.max_edge_dist_round, shear_load=self.load.shear_force * 1000, gap=self.end_to_sptd, shear_ecc=True, bolt_line_limit=2, beta_lg=self.beta_lg_sptd) # if self.connectivity in VALUES_CONN_1: if self.bolt.bolt_type == TYP_BEARING: if 8 * self.bolt.bolt_diameter_provided < self.t_sum_sptd: self.sptd_leg.grip_status = False self.sptd_leg.design_status = False if self.sptd_leg.length > self.cleat.leg_a_length or self.sptd_leg.design_status == False or self.sptd_leg.grip_status == False: self.sptd_leg.design_status = False count = 0 continue else: # self.cleat_angle_check(self) self.sptd_leg.design_status = True print(1, self.sptd_leg.bolt_force, self.bolt.bolt_capacity, self.bolt.bolt_diameter_provided, self.sptd_leg.bolts_required, self.sptd_leg.bolts_one_line) if self.sptd_leg.design_status is True: if self.sptd_leg.bolts_required > bolts_required_previous and count >= 1: self.bolt.bolt_diameter_provided = bolt_dia_previous self.sptd_leg.length = length_previous self.sptd_leg.height = height_previous self.sptd_leg.bolt_line = bolt_line_previous self.sptd_leg.bolts_one_line = bolts_one_line_previous self.sptd_leg.bolts_required = bolts_required_previous self.sptd_leg.bolt_capacity_red = bolt_capacity_red_previous self.sptd_leg.bolt_force = vres_previous self.sptd_leg.moment_demand = moment_demand_previous self.sptd_leg.pitch_provided = pitch_previous self.sptd_leg.gauge_provided = gauge_previous self.sptd_leg.edge_dist_provided = edge_dist_previous self.sptd_leg.end_dist_provided = end_dist_previous self.beta_lj_sptd = beta_lj_sptd_previous self.beta_lg_sptd = beta_lg_sptd_previous break bolt_dia_previous = self.bolt.bolt_diameter_provided length_previous = self.sptd_leg.length height_previous = self.sptd_leg.height bolt_line_previous = self.sptd_leg.bolt_line bolts_one_line_previous = self.sptd_leg.bolts_one_line bolts_required_previous = self.sptd_leg.bolts_required bolt_capacity_red_previous = self.sptd_leg.bolt_capacity_red vres_previous = self.sptd_leg.bolt_force moment_demand_previous = self.sptd_leg.moment_demand pitch_previous = self.sptd_leg.pitch_provided gauge_previous = self.sptd_leg.gauge_provided edge_dist_previous = self.sptd_leg.edge_dist_provided end_dist_previous = self.sptd_leg.end_dist_provided beta_lj_sptd_previous = self.beta_lj_sptd beta_lg_sptd_previous = self.beta_lg_sptd count += 1 else: pass self.bolt.calculate_bolt_spacing_limits(bolt_diameter_provided=self.bolt.bolt_diameter_provided, conn_plates_t_fu_fy=self.sptd_bolt_conn_plates_t_fu_fy,n=2) self.bolt.calculate_bolt_capacity(bolt_diameter_provided=self.bolt.bolt_diameter_provided, bolt_grade_provided=self.bolt.bolt_PC_provided, conn_plates_t_fu_fy=self.sptd_bolt_conn_plates_t_fu_fy, n_planes=2) if self.sptd_leg.length <= self.cleat.leg_a_length and self.sptd_leg.design_status == True: # self.spting_leg.end_dist_provided = self.cleat.leg_a_length - self.cleat.thickness - self.cleat.root_radius - \ # self.spting_leg.end_dist_provided self.sptd_leg.cleat_angle_check(self.sptd_leg.height, self.cleat.thickness, self.sptd_leg.bolts_one_line, self.sptd_leg.bolt_line, self.sptd_leg.gauge_provided, self.sptd_leg.edge_dist_provided, self.sptd_leg.pitch_provided, self.sptd_leg.end_dist_provided, self.bolt.dia_hole, self.sptd_leg.fu, self.sptd_leg.fy, self.sptd_leg.moment_demand, self.max_plate_height, self.load.shear_force * 1000) print("supd_des_st:", self.sptd_leg.design_status) else: # if self.sptd_leg.reason == "": # self.sptd_leg.reason = (": Req leg length is {} and Available width on flange side is {}" # .format(self.sptd_leg.length, self.cleat.leg_a_length)) self.sptd_leg.design_status = False if self.sptd_leg.design_status is False: self.design_status = False # logger.error(self.sptd_leg.reason) supporting_leg_check = False else: supporting_leg_check = self.select_bolt_dia_supporting(self) if supporting_leg_check: trial += 1 self.total_bolts_sptd = self.sptd_leg.bolts_one_line * self.sptd_leg.bolt_line self.total_bolts_spting = self.spting_leg.bolts_one_line * self.spting_leg.bolt_line ##### O U T P U T D I C T I O N A R Y F O R M A T ##### row = [int(self.bolt.bolt_diameter_provided), # 0-Bolt Diameter self.bolt.bolt_PC_provided, # 1-Bolt Grade self.cleat.designation, # 2-Cleat Angle designation self.cleat.thickness, # 3-Cleat Angle Thickness self.cleat.leg_a_length, # 4-Cleat angle leg size self.sptd_leg.bolts_one_line, # 5-Bolt rows on cleat angle supported leg self.sptd_leg.bolt_line, # 6-Bolt columns on cleat angle supported leg self.sptd_leg.height, # 7-Length of the cleat angle self.sptd_leg.bolt_force, # 8-Bolt Force on supported leg self.spting_leg.bolts_one_line, # 9-Bolt rows on cleat angle supporting leg self.spting_leg.bolt_line, # 10-Bolt columns on cleat angle supporting leg self.spting_leg.height, # 11-Length of the cleat angle self.spting_leg.bolt_force, # 12-Bolt Force on supporting leg self.total_bolts_sptd, # 13-Total bolts on supported leg self.total_bolts_spting, # 14-Total bolts on supporting leg self.sptd_leg.pitch_provided, # 15-Pitch provided on the supported leg self.sptd_leg.gauge_provided, # 16-Gauge provided on the supported leg self.sptd_leg.end_dist_provided, # 17-End Distance provided on the supported leg self.sptd_leg.edge_dist_provided, # 18-Edge Distance provided on the supported leg self.spting_leg.pitch_provided, # 19-Pitch provided on the supporting leg self.spting_leg.gauge_provided, # 20-Gauge provided on the supporting leg self.spting_leg.end_dist_provided, # 21-End Distance provided on the supporting leg self.spting_leg.edge_dist_provided, # 22-Edge Distance provided on the supporting leg self.bolt.bolt_shear_capacity, # 23-Bolt shear capacity on the supported leg self.bolt.bolt_bearing_capacity, # 24-Bolt bearing capacity on the supported leg self.bolt2.bolt_shear_capacity, # 25-Bolt shear capacity on the supporting leg self.bolt2.bolt_bearing_capacity, # 26-Bolt bearing capacity on the supporting leg self.cleat.root_radius, # 27-Cleat angle root radius self.sptd_leg.block_shear_capacity, # 28-Cleat angle block shear capacity self.sptd_leg.cleat_shear_capacity, # 29-Cleat angle shear yielding capacity self.sptd_leg.cleat_moment_capacity, # 30-Cleat angle moment capacity self.sptd_leg.moment_demand, # 31-Cleat angle moment demand trial] self.output.append(row) print("********* Trial {} ends here *************".format(trial)) if self.output == []: self.design_status = False if self.sptd_leg.design_status is False: self.bolt_capacity_disp_sptd = round( (self.bolt.bolt_capacity * self.beta_lj_sptd * self.beta_lg_sptd) / 1000, 2) if self.sptd_leg.grip_status == False: self.sptd_leg.reason = "Fails in grip length on supported side." if self.sptd_leg.reason == "": logger.info("{}rows {}columns {}mm diameter bolts needs leg length of {}" .format(self.sptd_leg.bolts_one_line, self.sptd_leg.bolt_line, self.bolt.bolt_diameter_provided, self.sptd_leg.length)) logger.info("Available width is {}".format(min(self.cleat.leg_a_length,self.available_length))) else: logger.error(self.sptd_leg.reason) elif self.spting_leg.design_status is False: self.bolt_capacity_disp_sptd = round( (self.bolt.bolt_capacity * self.beta_lj_sptd * self.beta_lg_sptd) / 1000, 2) self.bolt_capacity_disp_spting = round( (self.bolt2.bolt_capacity * self.beta_lj_spting * self.beta_lg_spting) / 1000, 2) if self.spting_leg.grip_status == False: self.spting_leg.reason = "Fails in grip length on supporting side." logger.error(self.spting_leg.reason) logger.error("The connection cannot be designed with provided bolt diameters or cleat angle list") else: self.select_optimum(self) # print("why repeat",self.bolt.bolt_diameter_provided,self.cleat.designation) self.for_3D_view(self) self.design_status = True self.sptd_leg.design_status = True self.spting_leg.design_status = True self.sptd_leg.grip_status = True self.spting_leg.grip_status = True def select_optimum(self): """This function sorts the list of available options and selects the combination with least leg size or thickness or number of bolts""" self.output.sort(key=lambda x: (x[4], x[3], x[13])) # print(self.output) print(self.output[0]) self.bolt.bolt_diameter_provided = self.output[0][0] self.bolt.bolt_PC_provided = self.output[0][1] self.cleat.designation = self.output[0][2] self.cleat.thickness = self.output[0][3] self.cleat.leg_a_length = self.output[0][4] self.cleat.leg_b_length = self.output[0][4] self.sptd_leg.bolts_one_line = self.output[0][5] self.sptd_leg.bolt_line = self.output[0][6] self.sptd_leg.height = self.output[0][7] self.sptd_leg.bolt_force = self.output[0][8] self.spting_leg.bolts_one_line = self.output[0][9] self.spting_leg.bolt_line = self.output[0][10] self.spting_leg.height = self.output[0][11] self.spting_leg.bolt_force = self.output[0][12] self.total_bolts_sptd = self.output[0][13] self.total_bolts_spting = self.output[0][14] self.sptd_leg.pitch_provided = self.output[0][15] self.sptd_leg.gauge_provided = self.output[0][16] self.sptd_leg.end_dist_provided = self.output[0][17] self.sptd_leg.edge_dist_provided = self.output[0][18] self.spting_leg.pitch_provided = self.output[0][19] self.spting_leg.gauge_provided = self.output[0][20] self.spting_leg.end_dist_provided = self.output[0][21] self.spting_leg.edge_dist_provided = self.output[0][22] self.bolt.bolt_shear_capacity = self.output[0][23] self.bolt.bolt_bearing_capacity = self.output[0][24] self.bolt2.bolt_shear_capacity = self.output[0][25] self.bolt2.bolt_bearing_capacity = self.output[0][26] self.cleat.root_radius = self.output[0][27] self.sptd_leg.block_shear_capacity = self.output[0][28] self.sptd_leg.cleat_shear_capacity = self.output[0][29] self.sptd_leg.cleat_moment_capacity = self.output[0][30] self.sptd_leg.moment_demand = self.output[0][31] self.get_bolt_PC(self) def select_bolt_dia_supporting(self): self.supporting_leg_check = False self.spting_bolt_conn_plates_t_fu_fy = [] self.spting_bolt_conn_plates_t_fu_fy.append((self.cleat.thickness, self.sptd_leg.fu, self.sptd_leg.fy)) if self.connectivity == VALUES_CONN_1[0]: self.spting_bolt_conn_plates_t_fu_fy.append((self.supporting_section.flange_thickness, self.supporting_section.fu, self.supporting_section.fy)) else: self.spting_bolt_conn_plates_t_fu_fy.append((self.supporting_section.web_thickness, self.supporting_section.fu, self.supporting_section.fy)) """ # while considering eccentricity, distance from bolt line to supporting member will be, # end_dist+gap or end_dist+root_radius+cleat_thickness # """ self.end_to_spting = self.cleat.thickness + self.cleat.root_radius # print(self.bolt.bolt_shear_capacity) self.bolt2.calculate_bolt_spacing_limits(bolt_diameter_provided=self.bolt.bolt_diameter_provided, conn_plates_t_fu_fy=self.spting_bolt_conn_plates_t_fu_fy,n=1) self.bolt2.calculate_bolt_capacity(bolt_diameter_provided=self.bolt.bolt_diameter_provided, bolt_grade_provided=self.bolt.bolt_PC_provided, conn_plates_t_fu_fy=self.spting_bolt_conn_plates_t_fu_fy, n_planes=1) self.l_j_spting = self.spting_leg.gauge_provided * (self.spting_leg.bolts_one_line - 1) if self.connectivity == VALUES_CONN_1[0]: self.t_sum_spting = self.supporting_section.flange_thickness + self.cleat.thickness else: self.t_sum_spting = self.supporting_section.web_thickness + self.cleat.thickness self.beta_lj_spting = IS800_2007.cl_10_3_3_1_bolt_long_joint(self.bolt.bolt_diameter_provided, self.l_j_spting) if self.bolt.bolt_type == TYP_BEARING: self.beta_lg_spting = IS800_2007.cl_10_3_3_2_bolt_large_grip(self.bolt.bolt_diameter_provided, self.t_sum_spting, self.l_j_spting) else: self.beta_lg_spting = 1.0 self.spting_leg.get_web_plate_details(bolt_dia=self.bolt.bolt_diameter_provided, web_plate_h_min=self.sptd_leg.height, web_plate_h_max=self.sptd_leg.height, bolt_capacity=self.bolt2.bolt_capacity, min_edge_dist=self.sptd_leg.edge_dist_provided, min_gauge=self.sptd_leg.gauge_provided, max_spacing=self.sptd_leg.gauge_provided, max_edge_dist=self.sptd_leg.edge_dist_provided, shear_load=self.load.shear_force * 1000 / 2, gap=self.end_to_spting, shear_ecc=True, bolt_line_limit=2, min_bolts_one_line=self.sptd_leg.bolts_one_line, min_bolt_line=1, beta_lg=self.beta_lg_spting, min_end_dist=self.bolt.min_end_dist_round) # if self.spting_leg.length > self.cleat.leg_a_length: # logger.info(": {}rows {}columns {}mm diameter bolts needs leg length of {}" # .format(self.spting_leg.bolts_one_line, self.spting_leg.bolt_line, # self.bolt2.bolt_diameter_provided, self.spting_leg.length)) # logger.info(": Available width of selected cleat angle leg is {}".format(self.cleat.leg_a_length)) # count = 0 # continue print("Supporting leg side: ", self.spting_leg.design_status, self.spting_leg.bolt_force, self.bolt2.bolt_capacity, self.bolt2.bolt_diameter_provided, self.spting_leg.bolts_required, self.spting_leg.bolts_one_line) if self.bolt.bolt_type == TYP_BEARING: if 8 * self.bolt.bolt_diameter_provided < self.t_sum_spting: self.spting_leg.grip_status = False self.spting_leg.design_status = False if self.spting_leg.design_status is False and self.cleat_list_thk and self.spting_leg.grip_status is True: self.cleat_list_thk = [x for x in self.cleat_list_thk if x != self.cleat.designation] # if not self.cleat_list_thk: # self.design_status = False # else: # self.select_bolt_dia_beam(self) self.design_status = False else: pass if self.spting_leg.length <= self.cleat.leg_a_length and self.spting_leg.design_status is True: # self.spting_leg.end_dist_provided = self.cleat.leg_a_length - self.cleat.thickness - self.cleat.root_radius - \ # self.spting_leg.end_dist_provided self.spting_leg.cleat_angle_check(self.spting_leg.height, self.cleat.thickness, self.spting_leg.bolts_one_line, self.spting_leg.bolt_line, self.spting_leg.gauge_provided, self.spting_leg.edge_dist_provided, self.spting_leg.pitch_provided, self.spting_leg.end_dist_provided, self.bolt2.dia_hole, self.spting_leg.fu, self.spting_leg.fy, self.spting_leg.moment_demand, self.max_plate_height, self.load.shear_force * 1000) else: if self.spting_leg.reason == "": self.spting_leg.reason = (": Req leg length is {} and available leg size of cleat angle is {}" .format(self.spting_leg.length, self.cleat.leg_a_length)) self.spting_leg.design_status = False if self.spting_leg.design_status is False: self.design_status = False # logger.error(self.spting_leg.reason) else: self.design_status = True self.supporting_leg_check = True return self.supporting_leg_check def get_bolt_PC(self): print(self.design_status, "Getting bolt grade") self.sptd_bolt_conn_plates_t_fu_fy = [] self.sptd_bolt_conn_plates_t_fu_fy.append((2 * self.cleat.thickness, self.sptd_leg.fu, self.sptd_leg.fy)) self.sptd_bolt_conn_plates_t_fu_fy.append( (self.supported_section.web_thickness, self.supported_section.fu, self.supported_section.fy)) self.spting_bolt_conn_plates_t_fu_fy = [] self.spting_bolt_conn_plates_t_fu_fy.append((self.cleat.thickness, self.sptd_leg.fu, self.sptd_leg.fy)) if self.connectivity == VALUES_CONN_1[0]: self.spting_bolt_conn_plates_t_fu_fy.append((self.supporting_section.flange_thickness, self.supporting_section.fu, self.supporting_section.fy)) else: self.spting_bolt_conn_plates_t_fu_fy.append((self.supporting_section.web_thickness, self.supporting_section.fu, self.supporting_section.fy)) self.l_j_sptd = self.sptd_leg.gauge_provided * (self.sptd_leg.bolts_one_line - 1) self.t_sum_sptd = self.supported_section.web_thickness + 2 * self.cleat.thickness self.l_j_spting = self.spting_leg.gauge_provided * (self.spting_leg.bolts_one_line - 1) if self.connectivity == VALUES_CONN_1[0]: self.t_sum_spting = self.supporting_section.flange_thickness + self.cleat.thickness else: self.t_sum_spting = self.supporting_section.web_thickness + self.cleat.thickness self.beta_lj_sptd = IS800_2007.cl_10_3_3_1_bolt_long_joint(self.bolt.bolt_diameter_provided, self.l_j_sptd) self.beta_lj_spting = IS800_2007.cl_10_3_3_1_bolt_long_joint(self.bolt.bolt_diameter_provided, self.l_j_spting) if self.bolt.bolt_type == TYP_BEARING: self.beta_lg_sptd = IS800_2007.cl_10_3_3_2_bolt_large_grip(self.bolt.bolt_diameter_provided, self.t_sum_sptd, self.l_j_sptd) self.beta_lg_spting = IS800_2007.cl_10_3_3_2_bolt_large_grip(self.bolt.bolt_diameter_provided, self.t_sum_spting, self.l_j_spting) else: self.beta_lg_sptd = 1.0 self.beta_lg_spting = 1.0 bolt_PC_previous = self.bolt.bolt_PC_provided for self.bolt.bolt_PC_provided in reversed(self.bolt.bolt_grade): # print(self.bolt.bolt_grade) self.bolt2.bolt_PC_provided = self.bolt.bolt_PC_provided # print(self.bolt2.bolt_PC_provided) count = 1 self.bolt.calculate_bolt_capacity(bolt_diameter_provided=self.bolt.bolt_diameter_provided, bolt_grade_provided=self.bolt.bolt_PC_provided, conn_plates_t_fu_fy=self.sptd_bolt_conn_plates_t_fu_fy, n_planes=2, e=self.sptd_leg.edge_dist_provided, p=self.sptd_leg.gauge_provided) self.bolt2.calculate_bolt_capacity(bolt_diameter_provided=self.bolt.bolt_diameter_provided, bolt_grade_provided=self.bolt.bolt_PC_provided, conn_plates_t_fu_fy=self.spting_bolt_conn_plates_t_fu_fy, n_planes=1, e=self.spting_leg.edge_dist_provided, p=self.spting_leg.gauge_provided) # print(self.bolt.bolt_capacity, self.sptd_leg.bolt_force, self.bolt2.bolt_capacity, self.spting_leg.bolt_force) if (self.bolt.bolt_capacity * self.beta_lj_sptd * self.beta_lg_sptd < self.sptd_leg.bolt_force or self.bolt2.bolt_capacity * self.beta_lj_spting * self.beta_lg_spting < self.spting_leg.bolt_force): self.bolt.bolt_PC_provided = bolt_PC_previous self.bolt2.bolt_PC_provided = bolt_PC_previous self.bolt.calculate_bolt_capacity(bolt_diameter_provided=self.bolt.bolt_diameter_provided, bolt_grade_provided=self.bolt.bolt_PC_provided, conn_plates_t_fu_fy=self.sptd_bolt_conn_plates_t_fu_fy, n_planes=2, e=self.sptd_leg.edge_dist_provided, p=self.sptd_leg.gauge_provided) self.bolt2.calculate_bolt_capacity(bolt_diameter_provided=self.bolt.bolt_diameter_provided, bolt_grade_provided=self.bolt.bolt_PC_provided, conn_plates_t_fu_fy=self.spting_bolt_conn_plates_t_fu_fy, n_planes=1, e=self.spting_leg.edge_dist_provided, p=self.spting_leg.gauge_provided) break bolt_PC_previous = self.bolt.bolt_PC_provided count += 1 self.bolt.calculate_bolt_spacing_limits(bolt_diameter_provided=self.bolt.bolt_diameter_provided, conn_plates_t_fu_fy=self.sptd_bolt_conn_plates_t_fu_fy,n=2) self.bolt2.calculate_bolt_spacing_limits(bolt_diameter_provided=self.bolt.bolt_diameter_provided, conn_plates_t_fu_fy=self.spting_bolt_conn_plates_t_fu_fy,n=1) self.bolt_capacity_disp_sptd = round((self.bolt.bolt_capacity * self.beta_lj_sptd * self.beta_lg_sptd)/1000, 2) self.bolt_capacity_disp_spting = round((self.bolt2.bolt_capacity * self.beta_lj_spting * self.beta_lg_spting)/1000, 2) def for_3D_view(self): self.design_status = True self.cleat = Angle(designation=self.cleat.designation,material_grade=self.cleat_material_grade) self.cleat.gauge_sptd = self.sptd_leg.gauge_provided self.cleat.pitch_sptd = self.sptd_leg.pitch_provided self.cleat.edge_sptd = self.sptd_leg.edge_dist_provided # self.cleat.end_sptd = self.sptd_leg.end_dist_provided print(self.sptd_leg.gap, self.cleat.thickness +self.cleat.root_radius) self.cleat.end_sptd =self.cleat.leg_a_length - max(self.sptd_leg.gap, self.cleat.thickness +self.cleat.root_radius)\ - self.sptd_leg.end_dist_provided - self.cleat.gauge_sptd*(self.sptd_leg.bolt_line-1) self.cleat.bolt_lines_sptd = self.sptd_leg.bolt_line self.cleat.bolt_one_line_sptd = self.sptd_leg.bolts_one_line self.cleat.gauge_spting = self.spting_leg.gauge_provided self.cleat.pitch_spting = self.spting_leg.pitch_provided self.cleat.edge_spting = self.spting_leg.edge_dist_provided # self.cleat.end_spting = self.spting_leg.end_dist_provided self.cleat.end_spting = self.cleat.leg_a_length - self.cleat.thickness - self.cleat.root_radius - \ self.spting_leg.end_dist_provided - self.cleat.gauge_spting*(self.spting_leg.bolt_line-1) self.cleat.bolt_lines_spting = self.spting_leg.bolt_line self.cleat.bolt_one_line_spting = self.spting_leg.bolts_one_line self.cleat.height = max(self.spting_leg.height, self.sptd_leg.height) self.cleat.gap = self.sptd_leg.gap logger.debug("=== End Of Design ===") def save_design(self, popup_summary): super(CleatAngleConnection, self).save_design(self) gamma_m0 = IS800_2007.cl_5_4_1_Table_5["gamma_m0"]['yielding'] # bolt_list = str(*self.bolt.bolt_diameter, sep=", ") if self.cleat_list_thk: self.report_cleat_angle = {KEY_DISP_SEC_PROFILE: "equaldp", # Image shall be save with this name.png in resource files KEY_DISP_SECSIZE_REPORT: self.cleat.designation, KEY_DISP_MATERIAL: self.cleat.material, KEY_DISP_FU: round(self.cleat.fu, 2), KEY_DISP_FY: round(self.cleat.fy, 2), KEY_REPORT_MASS: round(self.cleat.mass, 2), KEY_REPORT_AREA: round((self.cleat.area / 100), 2), KEY_REPORT_MAX_LEG_SIZE: round(self.cleat.max_leg, 2), KEY_REPORT_MIN_LEG_SIZE: round(self.cleat.min_leg, 2), KEY_REPORT_ANGLE_THK: round(self.cleat.thickness, 2), KEY_REPORT_R1: round(self.cleat.root_radius, 2), KEY_REPORT_R2: round(self.cleat.toe_radius, 2), KEY_REPORT_CY: round(self.cleat.Cy, 2), KEY_REPORT_CZ: round(self.cleat.Cz, 2), KEY_REPORT_IZ: round(self.cleat.mom_inertia_z / 10000, 2), KEY_REPORT_IY: round(self.cleat.mom_inertia_y / 10000, 2), KEY_REPORT_IU: round(self.cleat.mom_inertia_u / 10000, 2), KEY_REPORT_IV: round(self.cleat.mom_inertia_v / 10000, 2), KEY_REPORT_RZ: round(self.cleat.rad_of_gy_z / 10, 2), KEY_REPORT_RY: round((self.cleat.rad_of_gy_y) / 10, 2), KEY_REPORT_RU: round((self.cleat.rad_of_gy_u) / 10, 2), KEY_REPORT_RV: round((self.cleat.rad_of_gy_v) / 10, 2), KEY_REPORT_ZEZ: round(self.cleat.elast_sec_mod_z / 1000, 2), KEY_REPORT_ZEY: round(self.cleat.elast_sec_mod_y / 1000, 2), KEY_REPORT_ZPZ: round(self.cleat.plast_sec_mod_z / 1000, 2), KEY_REPORT_ZPY: round(self.cleat.elast_sec_mod_y / 1000, 2)} else: self.report_cleat_angle = {} self.report_input = \ {KEY_MAIN_MODULE: self.mainmodule, KEY_MODULE: self.module, KEY_CONN: self.connectivity, KEY_DISP_SHEAR: self.load.shear_force, "Supporting Section - Mechanical Properties": "TITLE", "Supporting Section Details": self.report_supporting, "Supported Section - Mechanical Properties": "TITLE", "Supported Section Details": self.report_supported, "Bolt Details - Input and Design Preference": "TITLE", KEY_DISP_D: str(list(np.int_(self.bolt.bolt_diameter))), KEY_DISP_GRD: str(self.bolt.bolt_grade), KEY_DISP_TYP: self.bolt.bolt_type, KEY_DISP_DP_BOLT_HOLE_TYPE: self.bolt.bolt_hole_type, KEY_DISP_DP_BOLT_SLIP_FACTOR_REPORT: self.bolt.mu_f, "Detailing - Design Preference": "TITLE", KEY_DISP_DP_DETAILING_EDGE_TYPE: self.bolt.edge_type, KEY_DISP_GAP: self.sptd_leg.gap, KEY_DISP_DP_DETAILING_CORROSIVE_INFLUENCES_BEAM: self.bolt.corrosive_influences, KEY_DISP_CLEAT_ANGLE_LIST: str(self.cleat_list), "Selected Section Details": self.report_cleat_angle } if not self.cleat_list_thk: self.report_input.pop("Selected Section Details") self.report_check = [] t1 = ('Selected', 'Selected Member Data', '|p{5cm}|p{2cm}|p{2cm}|p{2cm}|p{4cm}|') self.report_check.append(t1) gamma_m0 = IS800_2007.cl_5_4_1_Table_5["gamma_m0"]['yielding'] gamma_m1 = IS800_2007.cl_5_4_1_Table_5["gamma_m1"]['ultimate_stress'] if not self.cleat_list_thk: t1 = ('SubSection', 'Minimum Plate Thickness Check', '|p{4cm}|p{5cm}|p{5.5cm}|p{1.5cm}|') self.report_check.append(t1) t1 = (DISP_MIN_PLATE_THICK, min_plate_thk_req(self.supported_section.web_thickness), self.thickness_list,'Fail') self.report_check.append(t1) t1 = ('Available Length on Supporting Section', self.available_length, self.leg_lengths, 'Fail') self.report_check.append(t1) elif self.supported_section.design_status is True: t1 = ('SubSection', 'Initial Section Check', '|p{4cm}|p{5cm}|p{5.5cm}|p{1.5cm}|') self.report_check.append(t1) a = self.supported_section h = a.web_height t = a.web_thickness t1 = (KEY_DISP_SHEAR_YLD, self.load.shear_force, cl_8_4_shear_yielding_capacity_member(h, t, a.fy, gamma_m0, round(a.shear_yielding_capacity / 1000, 2)), get_pass_fail(self.load.shear_force, round(a.shear_yielding_capacity / 1000, 2), relation="lesser")) self.report_check.append(t1) t1 = (KEY_DISP_ALLOW_SHEAR, self.load.shear_force, allow_shear_capacity(round(a.shear_yielding_capacity / 1000, 2), round(a.low_shear_capacity / 1000, 2)), get_pass_fail(self.load.shear_force, round(a.low_shear_capacity / 1000, 2), relation="lesser")) self.report_check.append(t1) t1 = ('SubSection', 'Load Consideration', '|p{4cm}|p{5cm}|p{5.5cm}|p{1.5cm}|') self.report_check.append(t1) min_shear_load = min(40, round(0.15 * self.supported_section.shear_yielding_capacity / 0.6, 2)) applied_shear_force = max(self.load.shear_force, min_shear_load) t1 = (KEY_DISP_APPLIED_SHEAR_LOAD, self.load.shear_force, prov_shear_load(shear_input=self.load.shear_force, min_sc=min_shear_load, app_shear_load=applied_shear_force, shear_capacity_1=round(self.supported_section.shear_yielding_capacity / 1000, 2)), "") self.report_check.append(t1) for leg in [self.sptd_leg, self.spting_leg]: if self.sptd_leg.design_status == False and leg == self.spting_leg: continue if leg == self.sptd_leg: t1 = ('SubSection', 'Bolt Design - Connected to Beam', '|p{3cm}|p{5.5cm}|p{6cm}|p{1.5cm}|') connecting_plates = [self.cleat.thickness, self.supported_section.web_thickness] all_connecting_plates_tk = [i[0] for i in self.sptd_bolt_conn_plates_t_fu_fy] bolt=self.bolt bolt_shear_capacity_kn = round(self.bolt.bolt_shear_capacity / 1000, 2) if self.bolt.bolt_type == "Bearing Bolt": bolt_bearing_capacity_kn = round(self.bolt.bolt_bearing_capacity / 1000, 2) bolt_capacity_kn = round(self.bolt.bolt_capacity / 1000, 2) bolt_force_kn = round(self.sptd_leg.bolt_force / 1000, 2) bolt_capacity_red_kn = self.bolt_capacity_disp_sptd n_planes=2 beta_lj = self.beta_lj_sptd beta_lg = self.beta_lg_sptd else: t1 = ('SubSection', 'Bolt Design - Connected to Column', '|p{3.5cm}|p{5cm}|p{6cm}|p{1.5cm}|') if self.connectivity == VALUES_CONN_2[0]: connecting_plates = [self.cleat.thickness, self.supporting_section.web_thickness] else: connecting_plates = [self.cleat.thickness, self.supporting_section.flange_thickness] bolt=self.bolt2 all_connecting_plates_tk = [i[0] for i in self.spting_bolt_conn_plates_t_fu_fy] bolt_shear_capacity_kn = round(self.bolt2.bolt_shear_capacity / 1000, 2) if self.bolt2.bolt_bearing_capacity != 'N/A': bolt_bearing_capacity_kn = round(self.bolt2.bolt_bearing_capacity / 1000, 2) bolt_capacity_kn = round(self.bolt2.bolt_capacity / 1000, 2) bolt_force_kn = round(self.spting_leg.bolt_force / 1000, 2) bolt_capacity_red_kn = self.bolt_capacity_disp_spting n_planes=1 beta_lj = self.beta_lj_spting beta_lg = self.beta_lg_spting self.report_check.append(t1) t1 = (KEY_DISP_D, '', bolt.bolt_diameter_provided, '') self.report_check.append(t1) t1 = (KEY_DISP_GRD, '', bolt.bolt_grade_provided, '') self.report_check.append(t1) t1 = (KEY_DISP_CLEATANGLE, '',self.cleat.designation,'') self.report_check.append(t1) t6 = (DISP_NUM_OF_COLUMNS, '', leg.bolt_line, '') self.report_check.append(t6) t7 = (DISP_NUM_OF_ROWS, '', leg.bolts_one_line, '') self.report_check.append(t7) t1 = (DISP_MIN_PITCH, cl_10_2_2_min_spacing(bolt.bolt_diameter_provided,'pitch'), leg.gauge_provided, get_pass_fail(bolt.min_pitch, leg.gauge_provided, relation='leq')) self.report_check.append(t1) # if leg.design_status is True: t1 = (DISP_MAX_PITCH, cl_10_2_3_1_max_spacing(connecting_plates,'pitch'), leg.gauge_provided, get_pass_fail(bolt.max_spacing, leg.gauge_provided, relation='geq')) self.report_check.append(t1) t2 = (DISP_MIN_GAUGE, cl_10_2_2_min_spacing(bolt.bolt_diameter_provided,'gauge'), leg.pitch_provided if leg.pitch_provided > 0 else 'N/A', get_pass_fail(bolt.min_gauge, leg.pitch_provided, relation="leq")) self.report_check.append(t2) t2 = (DISP_MAX_GAUGE, cl_10_2_3_1_max_spacing(connecting_plates,'gauge'), leg.pitch_provided if leg.pitch_provided > 0 else 'N/A', get_pass_fail(bolt.max_spacing, leg.pitch_provided, relation="geq")) self.report_check.append(t2) t3 = (DISP_MIN_END, cl_10_2_4_2_min_edge_end_dist(d_0=bolt.dia_hole, edge_type=bolt.edge_type, parameter='end_dist'), leg.edge_dist_provided, get_pass_fail(bolt.min_end_dist, leg.edge_dist_provided, relation='leq')) self.report_check.append(t3) if leg == self.sptd_leg: t4 = (DISP_MAX_END, cl_10_2_4_3_max_edge_end_dist(self.bolt.single_conn_plates_t_fu_fy, bolt.corrosive_influences, parameter='end_dist'), leg.edge_dist_provided, get_pass_fail(bolt.max_end_dist, leg.edge_dist_provided, relation='geq')) self.report_check.append(t4) else: t4 = (DISP_MAX_END, cl_10_2_4_3_max_edge_end_dist(self.bolt2.single_conn_plates_t_fu_fy, bolt.corrosive_influences, parameter='end_dist'), leg.edge_dist_provided, get_pass_fail(bolt.max_end_dist, leg.edge_dist_provided, relation='geq')) self.report_check.append(t4) t3 = (DISP_MIN_EDGE, cl_10_2_4_2_min_edge_end_dist(d_0=bolt.dia_hole, edge_type=bolt.edge_type, parameter='edge_dist'), leg.end_dist_provided, get_pass_fail(bolt.min_edge_dist, leg.end_dist_provided, relation='leq')) self.report_check.append(t3) if leg == self.sptd_leg: t4 = (DISP_MAX_EDGE, cl_10_2_4_3_max_edge_end_dist(self.bolt.single_conn_plates_t_fu_fy, bolt.corrosive_influences, parameter='edge_dist'), leg.end_dist_provided, get_pass_fail(bolt.max_edge_dist, leg.end_dist_provided, relation="geq")) else: t4 = (DISP_MAX_EDGE, cl_10_2_4_3_max_edge_end_dist(self.bolt2.single_conn_plates_t_fu_fy, bolt.corrosive_influences, parameter='edge_dist'), leg.end_dist_provided, get_pass_fail(bolt.max_edge_dist, leg.end_dist_provided, relation="geq")) self.report_check.append(t4) if self.sptd_leg.reason == "Minimum end/edge distance is greater than max end/edge distance." or\ self.sptd_leg.reason == "Can't fit two bolts in one line. Select lower diameter." or \ self.sptd_leg.reason == "Minimum pitch/gauge distance is greater than max pitch/gauge distance.": pass else: if leg == self.sptd_leg: ecc = max(self.sptd_leg.gap, self.cleat.thickness + self.cleat.root_radius) + \ self.sptd_leg.end_dist_provided+self.sptd_leg.pitch_provided*(self.sptd_leg.bolt_line-1)/2 leg.get_vres(leg.bolts_one_line, leg.pitch_provided, leg.gauge_provided, leg.bolt_line, self.load.shear_force, self.load.axial_force, ecc=ecc, web_moment=0.0) t10 = (KEY_OUT_REQ_MOMENT_DEMAND_BOLT, '', moment_demand_req_bolt_force( shear_load=round(self.load.shear_force, 2), web_moment=0.0, ecc=leg.ecc, moment_demand=round(leg.moment_demand, 2)), '') else: print("ecc",self.spting_leg.end_dist_provided + self.cleat.thickness + self.cleat.root_radius) ecc = self.spting_leg.end_dist_provided + self.cleat.thickness + self.cleat.root_radius + \ self.spting_leg.pitch_provided * (self.spting_leg.bolt_line-1)/2 leg.get_vres(leg.bolts_one_line, leg.pitch_provided, leg.gauge_provided, leg.bolt_line, self.load.shear_force/2, self.load.axial_force/2, ecc=ecc, web_moment=0.0) t10 = (KEY_OUT_REQ_MOMENT_DEMAND_BOLT, '', moment_demand_req_bolt_force( shear_load=round(self.load.shear_force/2, 2), web_moment=0.0, ecc=leg.ecc, moment_demand=round(leg.moment_demand, 2)), '') self.report_check.append(t10) t10 = (KEY_OUT_REQ_PARA_BOLT, parameter_req_bolt_force(bolts_one_line=leg.bolts_one_line , gauge=leg.gauge_provided, ymax=round(leg.ymax, 2), xmax=round(leg.xmax, 2), bolt_line=leg.bolt_line, pitch=leg.pitch_provided, length_avail=leg.length_avail, conn='fin'), '', '') self.report_check.append(t10) t10 = (KEY_OUT_BOLT_FORCE, Vres_bolts(bolts_one_line=leg.bolts_one_line, ymax=round(leg.ymax, 2), xmax=round(leg.xmax, 2), bolt_line=leg.bolt_line, shear_load=round(self.load.shear_force, 2), axial_load=round(self.load.axial_force, 2), moment_demand=round(leg.moment_demand, 2), r=round(leg.sigma_r_sq / 1000, 2), vbv=round(leg.vbv, 2), tmv=round(leg.tmv, 2), tmh=round(leg.tmh, 2), abh=round(leg.abh, 2), vres=round(leg.bolt_force / 1000, 2)), '', '') self.report_check.append(t10) # else: # t3 = (KEY_OUT_BOLT_FORCE, force_in_bolt_due_to_load(P=round(self.load.shear_force, 2), # n=leg.bolts_one_line*leg.bolt_line, # T_ba=round(leg.bolt_force / 1000, 2), # load='shear'),'','') # self.report_check.append(t3) if bolt.bolt_type == TYP_BEARING: t1 = (KEY_OUT_DISP_BOLT_SHEAR, '', cl_10_3_3_bolt_shear_capacity(bolt.bolt_fu, n_planes, bolt.bolt_net_area, bolt.gamma_mb, bolt_shear_capacity_kn), '') self.report_check.append(t1) t8 = (KEY_DISP_KB, " ", cl_10_3_4_calculate_kb(leg.edge_dist_provided, leg.gauge_provided, bolt.dia_hole, bolt.bolt_fu, bolt.fu_considered), '') self.report_check.append(t8) t2 = (KEY_OUT_DISP_BOLT_BEARING, '', cl_10_3_4_bolt_bearing_capacity(bolt.kb, bolt.bolt_diameter_provided, self.sptd_bolt_conn_plates_t_fu_fy, bolt.gamma_mb, bolt_bearing_capacity_kn), '') self.report_check.append(t2) t3 = (KEY_OUT_DISP_BOLT_CAPACITY, '', cl_10_3_2_bolt_capacity(bolt_shear_capacity_kn, bolt_bearing_capacity_kn, bolt_capacity_kn), '') self.report_check.append(t3) else: kh_disp = round(bolt.kh, 2) t4 = (KEY_OUT_DISP_BOLT_SLIP, '', cl_10_4_3_HSFG_bolt_capacity(mu_f=bolt.mu_f, n_e=1, K_h=kh_disp, fub=bolt.bolt_fu, Anb=bolt.bolt_net_area, gamma_mf=bolt.gamma_mf, capacity=bolt_capacity_kn), '') self.report_check.append(t4) t10 = (KEY_OUT_LONG_JOINT, '', cl_10_3_3_1_long_joint_bolted_prov(leg.bolt_line, leg.bolts_one_line, leg.pitch_provided, leg.gauge_provided, bolt.bolt_diameter_provided, bolt_capacity_kn, bolt_capacity_red_kn, 'n_r'), "") self.report_check.append(t10) if self.bolt.bolt_type == TYP_BEARING: if leg.grip_status == True: grip_remarks = "Pass" else: grip_remarks = "Fail" else: grip_remarks = "N/A" t10 = (KEY_OUT_LARGE_GRIP, '', cl_10_3_3_2_large_grip_bolted_prov(sum(all_connecting_plates_tk),self.bolt.bolt_diameter_provided, beta_lg), grip_remarks) self.report_check.append(t10) if leg.grip_status == True: t13 = (KEY_OUT_BOLT_CAPACITY_REDUCED, '',bolt_red_capacity_prov(beta_lj, beta_lg,bolt_capacity_kn, bolt_capacity_red_kn,'b'), "") self.report_check.append(t13) t5 = (KEY_OUT_DISP_BOLT_CAPACITY, bolt_force_kn, bolt_capacity_red_kn, get_pass_fail(bolt_force_kn, bolt_capacity_red_kn, relation="lesser")) self.report_check.append(t5) # if self.sptd_leg.design_status == True: ################### # Cleat angle checks ################### if self.sptd_leg.grip_status== True and self.spting_leg.grip_status == True: t1 = ('SubSection', 'Cleat Angle Check', '|p{3.5cm}|p{7.0cm}|p{4.5cm}|p{1cm}|') self.report_check.append(t1) t1 = (DISP_MIN_CLEAT_HEIGHT, min_plate_ht_req(self.supported_section.depth, self.supported_section.root_radius, self.supported_section.flange_thickness,self.min_plate_height), self.sptd_leg.height, get_pass_fail(self.min_plate_height, self.sptd_leg.height, relation="leq")) self.report_check.append(t1) if self.connectivity == VALUES_CONN_1: t1 = (DISP_MAX_CLEAT_HEIGHT, max_plate_ht_req(self.connectivity, self.supported_section.depth, self.supported_section.flange_thickness, self.supported_section.root_radius, self.supported_section.notch_ht, self.max_plate_height), self.sptd_leg.height, get_pass_fail(self.max_plate_height, self.sptd_leg.height, relation="greater")) self.report_check.append(t1) else: t1 = (DISP_MAX_CLEAT_HEIGHT, max_plate_ht_req(self.connectivity, self.supporting_section.depth, self.supporting_section.flange_thickness, self.supporting_section.root_radius, 0.0, self.max_plate_height), self.sptd_leg.height, get_pass_fail(self.max_plate_height, self.sptd_leg.height, relation="greater")) self.report_check.append(t1) additional_length = max(self.sptd_leg.gap, self.cleat.thickness+self.cleat.root_radius) min_plate_length = additional_length + 2 * self.bolt.min_end_dist + \ (self.sptd_leg.bolt_line - 1) * self.bolt.min_pitch t1 = (DISP_MIN_LEG_LENGTH + ' (on supported leg)', min_angle_leg_length(self.bolt.min_pitch, self.bolt.min_end_dist,self.sptd_leg.gap, self.cleat.thickness,self.cleat.root_radius, self.sptd_leg.bolt_line, min_plate_length), self.cleat.leg_a_length, get_pass_fail(min_plate_length, self.cleat.leg_a_length, relation="lesser")) self.report_check.append(t1) min_plate_length = max(min_plate_length,2 * self.bolt2.min_end_dist + \ (self.spting_leg.bolt_line - 1) * self.bolt2.min_pitch) t1 = (DISP_MIN_LEG_LENGTH + ' (on supporting leg)', min_angle_leg_length(self.bolt.min_pitch, self.bolt.min_end_dist,0.0, self.cleat.thickness,self.cleat.root_radius, max(1,self.spting_leg.bolt_line), min_plate_length), self.cleat.leg_a_length, get_pass_fail(min_plate_length, self.cleat.leg_a_length, relation="lesser")) self.report_check.append(t1) t1 = (DISP_MIN_CLEAT_THK, min_plate_thk_req(self.supported_section.web_thickness,0.5), self.cleat.thickness, get_pass_fail(self.supported_section.web_thickness*0.5, self.cleat.thickness, relation="lesser")) self.report_check.append(t1) if self.sptd_leg.design_status == True: if self.design_status == True: h = self.cleat.height t = self.cleat.thickness elif self.spting_leg.height > 0 and self.spting_leg.thickness_provided > 0: h = max(self.sptd_leg.height, self.spting_leg.height) t = max(self.sptd_leg.thickness_provided,self.spting_leg.thickness_provided) else: h = self.sptd_leg.height t = self.sptd_leg.thickness_provided cleat_plastic_section_modulus = 2 * h ** 2 * t / 4 t1 = (KEY_DISP_SHEAR_YLD, '', cl_8_4_shear_yielding_capacity_member(h, t, self.cleat.fy, gamma_m0, round(self.sptd_leg.cleat_shear_capacity/1000,2),2), '') self.report_check.append(t1) t1 = (KEY_DISP_PLATE_BLK_SHEAR_SHEAR, '', cl_6_4_blockshear_capacity_member(Tdb=round(self.sptd_leg.block_shear_capacity / 1000, 2), stress='shear'), '') self.report_check.append(t1) cleat_shear_capacity = min(self.sptd_leg.cleat_shear_capacity,self.sptd_leg.block_shear_capacity) t1 = (KEY_DISP_SHEAR_CAPACITY, self.load.shear_force, cl_8_4_shear_capacity_member(round(self.sptd_leg.cleat_shear_capacity / 1000,2), 0.0, round(self.sptd_leg.block_shear_capacity / 1000, 2),'full'), get_pass_fail(self.load.shear_force, round(cleat_shear_capacity / 1000, 2), relation="lesser")) self.report_check.append(t1) t1 = (KEY_OUT_DISP_PLATE_MOM_CAPACITY, round(self.sptd_leg.moment_demand/1000, 2), cl_8_2_1_2_plastic_moment_capacity_member(beta_b=1.0, Z_p=round(cleat_plastic_section_modulus, 2), f_y=self.cleat.fy, gamma_m0=gamma_m0, Pmc=round(self.sptd_leg.cleat_moment_capacity/1000000,2)), get_pass_fail(self.sptd_leg.moment_demand, self.sptd_leg.cleat_moment_capacity, relation="lesser")) self.report_check.append(t1) Disp_2d_image = [] Disp_3D_image = "/ResourceFiles/images/3d.png" rel_path = str(sys.path[0]) rel_path = rel_path.replace("\\", "/") fname_no_ext = popup_summary['filename'] CreateLatex.save_latex(CreateLatex(), self.report_input, self.report_check, popup_summary, fname_no_ext, rel_path, Disp_2d_image, Disp_3D_image, module=self.module) # if __name__ == '__main__': # app = QApplication(sys.argv) # folder = r'C:\Users\Deepthi\Desktop\OsdagWorkspace' # # # folder_path = r'C:\Users\Win10\Desktop' # # folder_path = r'C:\Users\pc\Desktop' # # window = MainController(Ui_ModuleWindow, FinPlateConnection, folder_path) # from gui.ui_template import Ui_ModuleWindow # ui2 = Ui_ModuleWindow() # ui2.setupUi(ui2, CleatAngleConnection, folder) # ui2.show() # # app.exec_() # # sys.exit(app.exec_()) # try: # sys.exit(app.exec_()) # except BaseException as e: # print("ERROR", e) \ No newline at end of file diff --git a/design_type/main.py b/design_type/main.py deleted file mode 100644 index 59f6c8597..000000000 --- a/design_type/main.py +++ /dev/null @@ -1,457 +0,0 @@ -from Common import * -from utils.common.load import Load -from utils.common.component import * -from utils.common.Section_Properties_Calculator import * - -class Main(): - - def __init__(self): - pass - - ######################################### - # Design Preferences Functions Start - ######################################### - - def bolt_values(self, input_dictionary): - - if not input_dictionary or input_dictionary[KEY_TYP] == 'Bearing Bolt': - bolt_tension_type = 'Non pre-tensioned' - else: - bolt_tension_type = 'Pre-tensioned' - - if KEY_DP_BOLT_TYPE in input_dictionary.keys(): - bolt_tension_type = input_dictionary[KEY_DP_BOLT_TYPE] - - values = {KEY_DP_BOLT_TYPE: bolt_tension_type, KEY_DP_BOLT_HOLE_TYPE: 'Standard', - KEY_DP_BOLT_SLIP_FACTOR: '0.3'} - - for key in values.keys(): - if key in input_dictionary.keys(): - values[key] = input_dictionary[key] - - bolt = [] - - t1 = (KEY_DP_BOLT_TYPE, KEY_DISP_TYP, TYPE_COMBOBOX, ['Pre-tensioned', 'Non pre-tensioned'], values[KEY_DP_BOLT_TYPE]) - bolt.append(t1) - - t2 = (KEY_DP_BOLT_HOLE_TYPE, KEY_DISP_DP_BOLT_HOLE_TYPE, TYPE_COMBOBOX, ['Standard', 'Over-sized'], values[KEY_DP_BOLT_HOLE_TYPE]) - bolt.append(t2) - - t4 = (None, None, TYPE_ENTER, None, None) - bolt.append(t4) - - t5 = (None, KEY_DISP_DP_BOLT_DESIGN_PARA, TYPE_TITLE, None, None) - bolt.append(t5) - - t6 = (KEY_DP_BOLT_SLIP_FACTOR, KEY_DISP_DP_BOLT_SLIP_FACTOR, TYPE_COMBOBOX, - ['0.2', '0.5', '0.1', '0.25', '0.3', '0.33', '0.48', '0.52', '0.55'], values[KEY_DP_BOLT_SLIP_FACTOR]) - bolt.append(t6) - - t7 = (None, None, TYPE_ENTER, None, None) - bolt.append(t7) - - t8 = (None, "NOTE : If slip is permitted under the design load, design the bolt as" - "
a bearing bolt and select corresponding bolt grade.", TYPE_NOTE, None, None) - bolt.append(t8) - - t9 = ("textBrowser", "", TYPE_TEXT_BROWSER, BOLT_DESCRIPTION, None) - bolt.append(t9) - - return bolt - - def weld_values(self, input_dictionary): - - values = {KEY_DP_WELD_FAB: KEY_DP_FAB_SHOP, KEY_DP_WELD_MATERIAL_G_O: ''} - - if not input_dictionary or input_dictionary[KEY_MATERIAL] == 'Select Material': - pass - else: - values[KEY_DP_WELD_MATERIAL_G_O] = Material(input_dictionary[KEY_MATERIAL]).fu - # material_g_o = Material(input_dictionary[KEY_MATERIAL]).fu - - for key in values.keys(): - if key in input_dictionary.keys(): - values[key] = input_dictionary[key] - - weld = [] - - t1 = (KEY_DP_WELD_FAB, KEY_DISP_DP_WELD_FAB, TYPE_COMBOBOX, KEY_DP_WELD_FAB_VALUES, values[KEY_DP_WELD_FAB]) - weld.append(t1) - - t2 = (KEY_DP_WELD_MATERIAL_G_O, KEY_DISP_DP_WELD_MATERIAL_G_O, TYPE_TEXTBOX, None, values[KEY_DP_WELD_MATERIAL_G_O]) - weld.append(t2) - - t3 = ("textBrowser", "", TYPE_TEXT_BROWSER, WELD_DESCRIPTION, None) - weld.append(t3) - - return weld - - def detailing_values(self, input_dictionary): - - values = {KEY_DP_DETAILING_EDGE_TYPE: 'Sheared or hand flame cut', - KEY_DP_DETAILING_GAP: '10', - KEY_DP_DETAILING_CORROSIVE_INFLUENCES: 'No'} - - for key in values.keys(): - if key in input_dictionary.keys(): - values[key] = input_dictionary[key] - - detailing = [] - - t1 = (KEY_DP_DETAILING_EDGE_TYPE, KEY_DISP_DP_DETAILING_EDGE_TYPE, TYPE_COMBOBOX, - ['Sheared or hand flame cut', 'Rolled, machine-flame cut, sawn and planed'], - values[KEY_DP_DETAILING_EDGE_TYPE]) - detailing.append(t1) - - t2 = (KEY_DP_DETAILING_GAP, KEY_DISP_DP_DETAILING_GAP, TYPE_TEXTBOX, None, values[KEY_DP_DETAILING_GAP]) - detailing.append(t2) - - t3 = (KEY_DP_DETAILING_CORROSIVE_INFLUENCES, KEY_DISP_DP_DETAILING_CORROSIVE_INFLUENCES, TYPE_COMBOBOX, - ['No', 'Yes'], values[KEY_DP_DETAILING_CORROSIVE_INFLUENCES]) - detailing.append(t3) - - t4 = ("textBrowser", "", TYPE_TEXT_BROWSER, DETAILING_DESCRIPTION, None) - detailing.append(t4) - - return detailing - - def design_values(self, input_dictionary): - - values = {KEY_DP_DESIGN_METHOD: 'Limit State Design'} - - for key in values.keys(): - if key in input_dictionary.keys(): - values[key] = input_dictionary[key] - - design = [] - - t1 = (KEY_DP_DESIGN_METHOD, KEY_DISP_DP_DESIGN_METHOD, TYPE_COMBOBOX, - ['Limit State Design', 'Limit State (capacity based) Design', 'Working Stress Design'], - values[KEY_DP_DESIGN_METHOD]) - design.append(t1) - - return design - - def plate_connector_values(self, input_dictionary): - - if not input_dictionary or input_dictionary[KEY_MATERIAL] == 'Select Material': - material_grade = '' - fu = '' - fy_20 = '' - fy_20_40 = '' - fy_40 = '' - else: - material_grade = input_dictionary[KEY_MATERIAL] - material_attributes = Material(material_grade) - fu = material_attributes.fu - fy_20 = material_attributes.fy_20 - fy_20_40 = material_attributes.fy_20_40 - fy_40 = material_attributes.fy_40 - - if KEY_CONNECTOR_MATERIAL in input_dictionary.keys(): - material_grade = input_dictionary[KEY_CONNECTOR_MATERIAL] - material_attributes = Material(material_grade) - fu = material_attributes.fu - fy_20 = material_attributes.fy_20 - fy_20_40 = material_attributes.fy_20_40 - fy_40 = material_attributes.fy_40 - - connector = [] - - material = connectdb("Material", call_type="popup") - t1 = (KEY_CONNECTOR_MATERIAL, KEY_DISP_MATERIAL, TYPE_COMBOBOX, material, material_grade) - connector.append(t1) - - t2 = (KEY_CONNECTOR_FU, KEY_DISP_FU, TYPE_TEXTBOX, None, fu) - connector.append(t2) - - t3 = (KEY_CONNECTOR_FY_20, KEY_DISP_FY_20, TYPE_TEXTBOX, None, fy_20) - connector.append(t3) - - t3 = (KEY_CONNECTOR_FY_20_40, KEY_DISP_FY_20_40, TYPE_TEXTBOX, None, fy_20_40) - connector.append(t3) - - t3 = (KEY_CONNECTOR_FY_40, KEY_DISP_FY_40, TYPE_TEXTBOX, None, fy_40) - connector.append(t3) - - return connector - - - # def get_def_I_sec_properties(self): - # - # if 'default' in self: - # mass = '' - # area = '' - # moa_z = '' - # moa_y = '' - # rog_z = '' - # rog_y = '' - # em_z = '' - # em_y = '' - # pm_z = '' - # pm_y = '' - # I_t = '' - # I_w = '' - # image = VALUES_IMG_BEAM[0] - # d = {'Label_11': str(mass), - # 'Label_12': str(area), - # 'Label_13': str(moa_z), - # 'Label_14': str(moa_y), - # 'Label_15': str(rog_z), - # 'Label_16': str(rog_y), - # 'Label_17': str(em_z), - # 'Label_18': str(em_y), - # 'Label_19': str(pm_z), - # 'Label_20': str(pm_y), - # 'Label_21': str(I_t), - # 'Label_22': str(I_w), - # KEY_IMAGE: image - # } - # - # return d - - - def get_I_sec_properties(self): - - if '' in self: - mass = '' - area = '' - moa_z = '' - moa_y = '' - rog_z = '' - rog_y = '' - em_z = '' - em_y = '' - pm_z = '' - pm_y = '' - I_t = '' - I_w = '' - image = '' - - else: - D = float(self[0]) - B = float(self[1]) - t_w = float(self[2]) - t_f = float(self[3]) - sl = float(self[4]) - - sec_prop = I_sectional_Properties() - mass = sec_prop.calc_Mass(D, B, t_w, t_f) - area = sec_prop.calc_Area(D, B, t_w, t_f) - moa_z = sec_prop.calc_MomentOfAreaZ(D, B, t_w, t_f) - moa_y = sec_prop.calc_MomentOfAreaY(D, B, t_w, t_f) - rog_z = sec_prop.calc_RogZ(D, B, t_w, t_f) - rog_y = sec_prop.calc_RogY(D, B, t_w, t_f) - em_z = sec_prop.calc_ElasticModulusZz(D, B, t_w, t_f) - em_y = sec_prop.calc_ElasticModulusZy(D, B, t_w, t_f) - pm_z = sec_prop.calc_PlasticModulusZpz(D, B, t_w, t_f) - pm_y = sec_prop.calc_PlasticModulusZpy(D, B, t_w, t_f) - I_t = sec_prop.calc_TorsionConstantIt(D,B,t_w,t_f) - I_w = sec_prop.calc_WarpingConstantIw(D,B,t_w, t_f) - if sl != 90: - image = VALUES_IMG_BEAM[0] - else: - image = VALUES_IMG_BEAM[1] - - d = {'Label_11': str(mass), - 'Label_12': str(area), - 'Label_13': str(moa_z), - 'Label_14': str(moa_y), - 'Label_15': str(rog_z), - 'Label_16': str(rog_y), - 'Label_17': str(em_z), - 'Label_18': str(em_y), - 'Label_19': str(pm_z), - 'Label_20': str(pm_y), - 'Label_21': str(I_t), - 'Label_22': str(I_w), - KEY_IMAGE: image - } - - return d - - def get_SHS_RHS_properties(self): - - if '' in self: - mass = '' - area = '' - moa_z = '' - moa_y = '' - rog_z = '' - rog_y = '' - em_z = '' - em_y = '' - pm_z = '' - pm_y = '' - I_t = '' - I_w = '' - image = '' - - else: - D = float(self[0]) - B = float(self[1]) - t_w = float(self[2]) - t_f = float(self[2]) - sl = 0.0 - - sec_prop = SHS_RHS_Properties() - mass = sec_prop.calc_Mass(D, B, t_w, t_f) - area = sec_prop.calc_Area(D, B, t_w, t_f) - moa_z = sec_prop.calc_MomentOfAreaZ(D, B, t_w, t_f) - moa_y = sec_prop.calc_MomentOfAreaY(D, B, t_w, t_f) - rog_z = sec_prop.calc_RogZ(D, B, t_w, t_f) - rog_y = sec_prop.calc_RogY(D, B, t_w, t_f) - em_z = sec_prop.calc_ElasticModulusZz(D, B, t_w, t_f) - em_y = sec_prop.calc_ElasticModulusZy(D, B, t_w, t_f) - pm_z = sec_prop.calc_PlasticModulusZpz(D, B, t_w, t_f) - pm_y = sec_prop.calc_PlasticModulusZpy(D, B, t_w, t_f) - I_t = sec_prop.calc_TorsionConstantIt(D,B,t_w,t_f) - I_w = sec_prop.calc_WarpingConstantIw(D,B,t_w, t_f) - if D == B: - image = VALUES_IMG_HOLLOWSECTION[0] - else: - image = VALUES_IMG_HOLLOWSECTION[1] - - d = {'Label_HS_11': str(mass), - 'Label_HS_12': str(area), - 'Label_HS_13': str(moa_z), - 'Label_HS_14': str(moa_y), - 'Label_HS_15': str(rog_z), - 'Label_HS_16': str(rog_y), - 'Label_HS_17': str(em_z), - 'Label_HS_18': str(em_y), - 'Label_HS_19': str(pm_z), - 'Label_HS_20': str(pm_y), - 'Label_HS_21': str(I_t), - 'Label_HS_22': str(I_w), - KEY_IMAGE: image - } - - return d - - def get_CHS_properties(self): - - if '' in self: - mass = '' - area = '' - moa_z = '' - moa_y = '' - rog_z = '' - rog_y = '' - em_z = '' - em_y = '' - pm_z = '' - pm_y = '' - I_t = '' - I_w = '' - image = '' - - else: - D = float(self[1]) - B = float(self[1]) - t_w = float(self[2]) - t_f = float(self[2]) - sl = 0.0 - - sec_prop = CHS_Properties() - mass = sec_prop.calc_Mass(D, B, t_w, t_f) - area = sec_prop.calc_Area(D, B, t_w, t_f) - internal_vol = 0.0 - moa_z = sec_prop.calc_MomentOfAreaZ(D, B, t_w, t_f) - moa_y = sec_prop.calc_MomentOfAreaY(D, B, t_w, t_f) - rog_z = sec_prop.calc_RogZ(D, B, t_w, t_f) - rog_y = sec_prop.calc_RogY(D, B, t_w, t_f) - em_z = sec_prop.calc_ElasticModulusZz(D, B, t_w, t_f) - em_y = sec_prop.calc_ElasticModulusZy(D, B, t_w, t_f) - pm_z = sec_prop.calc_PlasticModulusZpz(D, B, t_w, t_f) - pm_y = sec_prop.calc_PlasticModulusZpy(D, B, t_w, t_f) - I_t = sec_prop.calc_TorsionConstantIt(D,B,t_w,t_f) - I_w = sec_prop.calc_WarpingConstantIw(D,B,t_w, t_f) - image = VALUES_IMG_HOLLOWSECTION[2] - - d = {'Label_CHS_11': str(mass), - 'Label_CHS_12': str(area), - 'Label_CHS_13': str(internal_vol), - 'Label_HS_14': str(moa_z), - # 'Label_HS_14': str(moa_y), - 'Label_HS_15': str(rog_z), - # 'Label_16': str(rog_y), - 'Label_HS_16': str(em_z), - # 'Label_18': str(em_y), - # 'Label_19': str(pm_z), - # 'Label_20': str(pm_y), - 'Label_21': str(I_t), - 'Label_22': str(I_w), - KEY_IMAGE: image - } - - return d - - def change_source(self): - - designation = self[0] - source = 'Custom' - if designation in connectdb("Columns", call_type="dropdown"): - source = get_source("Columns", designation) - elif designation in connectdb("Beams", call_type="dropdown"): - source = get_source("Beams", designation) - elif designation in connectdb("Angles", call_type="dropdown"): - source = get_source("Angles", designation) - elif designation in connectdb("Channels", call_type="dropdown"): - source = get_source("Channels", designation) - - d = {KEY_SOURCE: str(source)} - return d - - - @staticmethod - def grdval_customized(): - b = VALUES_GRD_CUSTOMIZED - return b - - @staticmethod - def diam_bolt_customized(): - c = connectdb1() - return c - - @staticmethod - def plate_thick_customized(): - d = VALUES_PLATETHK_CUSTOMIZED - return d - - def generate_missing_fields_error_string(self, missing_fields_list): - """ - Args: - missing_fields_list: list of fields that are not selected or entered - Returns: - error string that has to be displayed - """ - # The base string which should be displayed - information = "Please input the following required field" - if len(missing_fields_list) > 1: - # Adds 's' to the above sentence if there are multiple missing input fields - information += "s" - information += ": " - # Loops through the list of the missing fields and adds each field to the above sentence with a comma - - for item in missing_fields_list: - information = information + item + ", " - - # Removes the last comma - information = information[:-2] - information += "." - - return information - - def set_input_values(self, design_dictionary): - pass - - def call_3DModel(self, ui, bgcolor): - from PyQt5.QtWidgets import QCheckBox - from PyQt5.QtCore import Qt - for chkbox in ui.frame.children(): - if chkbox.objectName() == 'Model': - continue - if isinstance(chkbox, QCheckBox): - chkbox.setChecked(Qt.Unchecked) - ui.commLogicObj.display_3DModel("Model", bgcolor) diff --git a/design_type/member.py b/design_type/member.py deleted file mode 100644 index 66215bedb..000000000 --- a/design_type/member.py +++ /dev/null @@ -1,1753 +0,0 @@ -from Common import * -from utils.common.load import Load -from utils.common.component import * -from utils.common.Section_Properties_Calculator import * -from design_type.main import Main - -class Member(Main): - - def __init__(self): - super(Member, self).__init__() - - ######################################## - # Design Preference Functions Start - ######################################## - def df_conn_image(self): - - "Function to populate section size based on the type of section " - img = self[0] - if img == VALUES_SEC_PROFILE_2[0]: - return VALUES_IMG_TENSIONBOLTED[0] - elif img == VALUES_SEC_PROFILE_2[1]: - return VALUES_IMG_TENSIONBOLTED[1] - elif img == VALUES_SEC_PROFILE_2[2]: - return VALUES_IMG_TENSIONBOLTED[2] - elif img == VALUES_SEC_PROFILE_2[3]: - return VALUES_IMG_TENSIONBOLTED[3] - else: - return VALUES_IMG_TENSIONBOLTED[4] - - - def tab_angle_section(self, input_dictionary): - - "In design preference, it shows other properties of section used " - "In design preference, it shows other properties of section used " - if not input_dictionary or input_dictionary[KEY_SECSIZE] == [] or \ - input_dictionary[KEY_MATERIAL] == 'Select Material' or \ - input_dictionary[KEY_SEC_PROFILE] not in ['Angles', 'Back to Back Angles', 'Star Angles']: - designation = '' - material_grade = '' - section_profile = '' - l = '' - fu = '' - fy = '' - mass = '' - area = '' - a = '' - b = '' - thickness = '' - root_radius = '' - toe_radius = '' - plate_thk = '' - Cz = '' - Cy = '' - mom_inertia_z = '' - mom_inertia_y = '' - mom_inertia_u = '' - mom_inertia_v = '' - rad_of_gy_z = '' - rad_of_gy_y = '' - rad_of_gy_u = '' - rad_of_gy_v = '' - elast_sec_mod_z = '' - elast_sec_mod_y = '' - plast_sec_mod_z = '' - plast_sec_mod_y = '' - torsional_rigidity = '' - Type = '' - source = 'Custom' - m_o_e = "200" - m_o_r = "76.9" - p_r = "0.3" - t_e = "12" - image = '' - else: - designation = str(input_dictionary[KEY_SECSIZE][0]) - material_grade = str(input_dictionary[KEY_MATERIAL]) - section_profile = str(input_dictionary[KEY_SEC_PROFILE]) - l = str(input_dictionary[KEY_LOCATION]) - Angle_attributes = Angle(designation,material_grade) - source = str(Angle_attributes.source) - fu = str(Angle_attributes.fu) - fy = str(Angle_attributes.fy) - a = (Angle_attributes.a) - b = (Angle_attributes.b) - thickness = (Angle_attributes.thickness) - root_radius = str(Angle_attributes.root_radius) - toe_radius = str(Angle_attributes.toe_radius) - plate_thk = float(input_dictionary[KEY_PLATETHK][0]) - Type = str(Angle_attributes.type) - source = str(Angle_attributes.source) - m_o_e = "200" - m_o_r = "76.9" - p_r = "0.3" - t_e = "12" - if section_profile == 'Angles': - mass = str(round((Angle_attributes.mass), 2)) - area = str(round((Angle_attributes.area / 100), 2)) - Cz = str(round((Angle_attributes.Cz / 10), 2)) - Cy = str(round((Angle_attributes.Cy / 10), 2)) - mom_inertia_z = str(round((Angle_attributes.mom_inertia_z) / 10000, 2)) - mom_inertia_y = str(round((Angle_attributes.mom_inertia_y) / 10000, 2)) - mom_inertia_u = str(round((Angle_attributes.mom_inertia_u) / 10000, 2)) - mom_inertia_v = str(round((Angle_attributes.mom_inertia_v) / 10000, 2)) - rad_of_gy_z = str(round((Angle_attributes.rad_of_gy_z / 10), 2)) - rad_of_gy_y = str(round((Angle_attributes.rad_of_gy_y / 10), 2)) - rad_of_gy_u = str(round((Angle_attributes.rad_of_gy_u / 10), 2)) - rad_of_gy_v = str(round((Angle_attributes.rad_of_gy_v / 10), 2)) - elast_sec_mod_z = str(round((Angle_attributes.elast_sec_mod_z / 1000), 2)) - elast_sec_mod_y = str(round((Angle_attributes.elast_sec_mod_y / 1000), 2)) - plast_sec_mod_z = str(round((Angle_attributes.plast_sec_mod_z / 1000), 2)) - plast_sec_mod_y = str(round((Angle_attributes.plast_sec_mod_y / 1000), 2)) - torsional_rigidity = str(round((Angle_attributes.It / 10000), 2)) - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[0] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[0] - - else: - if section_profile == "Back to Back Angles": - print(section_profile, "hjcxhf") - Angle_attributes = BBAngle_Properties() - Angle_attributes.data(designation, material_grade) - if l == "Long Leg": - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[1] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[1] - Cz = str(Angle_attributes.calc_Cz(a, b,thickness, l)) - Cy = "N/A" - else: - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[2] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[2] - Cy = "N/A" - Cz = str(Angle_attributes.calc_Cz(a, b,thickness, l)) - mass = str(Angle_attributes.calc_Mass(a, b, thickness, l)) - area = str(Angle_attributes.calc_Area(a, b, thickness, l)) - mom_inertia_z = str(Angle_attributes.calc_MomentOfAreaZ(a, b, thickness, l, plate_thk)) - mom_inertia_y = str(Angle_attributes.calc_MomentOfAreaY(a, b, thickness, l, plate_thk)) - mom_inertia_u = str(Angle_attributes.calc_MomentOfAreaY(a, b, thickness, l, plate_thk)) - mom_inertia_v = str(Angle_attributes.calc_MomentOfAreaZ(a, b, thickness, l, plate_thk)) - rad_of_gy_z = str(Angle_attributes.calc_RogZ(a, b, thickness, l, plate_thk)) - rad_of_gy_y = str(Angle_attributes.calc_RogY(a, b, thickness, l, plate_thk)) - rad_of_gy_u = str(Angle_attributes.calc_RogY(a, b, thickness, l, plate_thk)) - rad_of_gy_v = str(Angle_attributes.calc_RogZ(a, b, thickness, l, plate_thk)) - elast_sec_mod_z = str(Angle_attributes.calc_ElasticModulusZz(a, b, thickness, l, plate_thk)) - elast_sec_mod_y = str(Angle_attributes.calc_ElasticModulusZy(a, b, thickness, l, plate_thk)) - plast_sec_mod_z = str(Angle_attributes.calc_PlasticModulusZpz(a, b, thickness, l, plate_thk)) - plast_sec_mod_y = str(Angle_attributes.calc_PlasticModulusZpy(a, b, thickness, l, plate_thk)) - torsional_rigidity = str(Angle_attributes.calc_TorsionConstantIt(a, b, thickness, l)) - else: # "Star Angles": - Angle_attributes = SAngle_Properties() - # Angle_attributes.data(designation, material_grade) - if l == "Long Leg": - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[3] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[3] - else: - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[4] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[4] - Cz = "N/A" - Cy = "N/A" - mass = str(Angle_attributes.calc_Mass(a, b,thickness, l)) - area = str(Angle_attributes.calc_Area(a, b, thickness, l)) - mom_inertia_z = str(Angle_attributes.calc_MomentOfAreaZ(a, b, thickness, l, plate_thk)) - mom_inertia_y = str(Angle_attributes.calc_MomentOfAreaY(a, b, thickness, l, plate_thk)) - mom_inertia_u = str(Angle_attributes.calc_MomentOfAreaU(a, b, thickness, l, plate_thk)) - mom_inertia_v = str(Angle_attributes.calc_MomentOfAreaV(a, b, thickness, l, plate_thk)) - rad_of_gy_z = str(Angle_attributes.calc_RogZ(a, b, thickness, l, plate_thk)) - rad_of_gy_y = str(Angle_attributes.calc_RogY(a, b, thickness, l, plate_thk)) - rad_of_gy_u = str(Angle_attributes.calc_RogU(a, b, thickness, l, plate_thk)) - rad_of_gy_v = str(Angle_attributes.calc_RogV(a, b, thickness, l, plate_thk)) - elast_sec_mod_z = str(Angle_attributes.calc_ElasticModulusZz(a, b, thickness, l,plate_thk)) - elast_sec_mod_y = str(Angle_attributes.calc_ElasticModulusZy(a, b, thickness, l,plate_thk)) - plast_sec_mod_z = str(Angle_attributes.calc_PlasticModulusZpz(a, b, thickness, l,plate_thk)) - plast_sec_mod_y = str(Angle_attributes.calc_PlasticModulusZpy(a, b, thickness, l,plate_thk)) - torsional_rigidity = str(Angle_attributes.calc_TorsionConstantIt(a, b, thickness, l)) - - # if KEY_SEC_MATERIAL in input_dictionary.keys(): - # material_grade = input_dictionary[KEY_SEC_MATERIAL] - # material_attributes = Material(material_grade) - # fu = material_attributes.fu - # fy = material_attributes.fy - - section = [] - - if input_dictionary: - designation_list = input_dictionary[KEY_SECSIZE] - else: - designation_list = [] - - t0 = (KEY_SECSIZE, KEY_DISP_DESIGNATION, TYPE_COMBOBOX, designation_list, designation) - section.append(t0) - - t1 = (KEY_SECSIZE_SELECTED, KEY_DISP_DESIGNATION, TYPE_TEXTBOX, None, designation) - section.append(t1) - - t1 = (KEY_SEC_PROFILE, KEY_DISP_SEC_PROFILE, TYPE_TEXTBOX, None, section_profile) - section.append(t1) - - t1 = (KEY_LOCATION, KEY_DISP_LOCATION, TYPE_TEXTBOX, None, l) - section.append(t1) - - t2 = (None, KEY_DISP_MECH_PROP, TYPE_TITLE, None, None) - section.append(t2) - - material = connectdb("Material", call_type="popup") - t34 = (KEY_SEC_MATERIAL, KEY_DISP_MATERIAL, TYPE_COMBOBOX, material, material_grade) - section.append(t34) - - t3 = (KEY_SEC_FU, KEY_DISP_FU, TYPE_TEXTBOX, None, fu) - section.append(t3) - - t4 = (KEY_SEC_FY, KEY_DISP_FY, TYPE_TEXTBOX, None, fy) - section.append(t4) - - t15 = ('Label_27', KEY_DISP_MOD_OF_ELAST, TYPE_TEXTBOX, None, m_o_e) - section.append(t15) - - t16 = ('Label_28', KEY_DISP_MOD_OF_RIGID, TYPE_TEXTBOX, None, m_o_r) - section.append(t16) - - t31 = ('Label_25', KEY_DISP_POISSON_RATIO, TYPE_TEXTBOX, None, p_r) - section.append(t31) - - t32 = ('Label_26', KEY_DISP_THERMAL_EXP, TYPE_TEXTBOX, None, t_e) - section.append(t32) - - t14 = ('Label_6', KEY_DISP_TYPE, TYPE_COMBOBOX, ['Rolled', 'Welded'], Type) - section.append(t14) - - t29 = (KEY_SOURCE, KEY_DISP_SOURCE, TYPE_TEXTBOX, None, source) - section.append(t29) - - t13 = (None, None, TYPE_BREAK, None, None) - section.append(t13) - - t5 = (None, KEY_DISP_DIMENSIONS, TYPE_TITLE, None, None) - section.append(t5) - - t6 = ('Label_1', KEY_DISP_A, TYPE_TEXTBOX, None, a) - section.append(t6) - - t6 = ('Label_2', KEY_DISP_B, TYPE_TEXTBOX, None, b) - section.append(t6) - - t8 = ('Label_3', KEY_DISP_LEG_THK, TYPE_TEXTBOX, None, thickness) - section.append(t8) - - t11 = ('Label_4', KEY_DISP_ROOT_R, TYPE_TEXTBOX, None, root_radius) - section.append(t11) - - t12 = ('Label_5', KEY_DISP_TOE_R, TYPE_TEXTBOX, None, toe_radius) - section.append(t12) - - t12 = ('Label_0', KEY_DISP_DPPLATETHK, TYPE_COMBOBOX, VALUES_PLATETHK_CUSTOMIZED, plate_thk) - section.append(t12) - - t17 = (None, KEY_DISP_SEC_PROP, TYPE_TITLE, None, None) - section.append(t17) - - t18 = ('Label_9', KEY_DISP_MASS, TYPE_TEXTBOX, None, mass) - section.append(t18) - - t19 = ('Label_10', KEY_DISP_AREA, TYPE_TEXTBOX, None, area) - section.append(t19) - - t18 = ('Label_7', KEY_DISP_Cz, TYPE_TEXTBOX, None, Cz) - section.append(t18) - - t19 = ('Label_8', KEY_DISP_Cy, TYPE_TEXTBOX, None, Cy) - section.append(t19) - - t20 = ('Label_11', KEY_DISP_MOA_IZ, TYPE_TEXTBOX, None, mom_inertia_z) - section.append(t20) - - t21 = ('Label_12', KEY_DISP_MOA_IY, TYPE_TEXTBOX, None, mom_inertia_y) - section.append(t21) - - # t13 = (None, None, TYPE_BREAK, None, None) - # section.append(t13) - # - # - # t18 = (None, None, TYPE_ENTER, None, None) - # section.append(t18) - - # t17 = (None, KEY_DISP_SEC_PROP, TYPE_TITLE, None, None) - # section.append(t17) - - t22 = ('Label_13', KEY_DISP_MOA_IU, TYPE_TEXTBOX, None, mom_inertia_u) - section.append(t22) - - t23 = ('Label_14', KEY_DISP_MOA_IV, TYPE_TEXTBOX, None, mom_inertia_v) - section.append(t23) - - t22 = ('Label_15', KEY_DISP_ROG_RZ, TYPE_TEXTBOX, None, rad_of_gy_z) - section.append(t22) - - t23 = ('Label_16', KEY_DISP_ROG_RY, TYPE_TEXTBOX, None, rad_of_gy_y) - section.append(t23) - - t13 = (None, None, TYPE_BREAK, None, None) - section.append(t13) - - t33 = (KEY_IMAGE, None, TYPE_IMAGE, None, image) - section.append(t33) - - t17 = (None, KEY_DISP_SEC_PROP, TYPE_TITLE, None, None) - section.append(t17) - - t22 = ('Label_17', KEY_DISP_ROG_RU, TYPE_TEXTBOX, None, rad_of_gy_u) - section.append(t22) - - t23 = ('Label_18', KEY_DISP_ROG_RV, TYPE_TEXTBOX, None, rad_of_gy_v) - section.append(t23) - - t24 = ('Label_19', KEY_DISP_EM_ZZ, TYPE_TEXTBOX, None, elast_sec_mod_z) - section.append(t24) - - t25 = ('Label_20', KEY_DISP_EM_ZY, TYPE_TEXTBOX, None, elast_sec_mod_y) - section.append(t25) - - t26 = ('Label_21', KEY_DISP_PM_ZPZ, TYPE_TEXTBOX, None, plast_sec_mod_z) - section.append(t26) - - t27 = ('Label_22', KEY_DISP_PM_ZPY, TYPE_TEXTBOX, None, plast_sec_mod_y) - section.append(t27) - - t27 = ('Label_23', KEY_DISP_It, TYPE_TEXTBOX, None, torsional_rigidity) - - section.append(t27) - - return section - - def tab_channel_section(self, input_dictionary): - - "In design preference, it shows other properties of section used " - "In design preference, it shows other properties of section used " - if not input_dictionary or input_dictionary[KEY_SECSIZE] == [] or \ - input_dictionary[KEY_MATERIAL] == 'Select Material' or \ - input_dictionary[KEY_SEC_PROFILE] not in ['Channels', 'Back to Back Channels']: - designation = '' - material_grade = '' - section_profile = '' - l = '' - fu = '' - fy = '' - mass = '' - area = '' - f_w = '' - f_t = '' - w_h = '' - w_t = '' - flange_slope = '' - root_radius = '' - toe_radius = '' - plate_thk = '' - C_y = '' - mom_inertia_z = '' - mom_inertia_y = '' - rad_of_gy_z = '' - rad_of_gy_y = '' - elast_sec_mod_z = '' - elast_sec_mod_y = '' - plast_sec_mod_z = '' - plast_sec_mod_y = '' - source = 'Custom' - m_o_e = "200" - m_o_r = "76.9" - p_r = "0.3" - t_e = "12" - Type='Rolled' - image = '' - It = "" - Iw = "" - - else: - designation = str(input_dictionary[KEY_SECSIZE][0]) - material_grade = str(input_dictionary[KEY_MATERIAL]) - section_profile = str(input_dictionary[KEY_SEC_PROFILE]) - l = str(input_dictionary[KEY_LOCATION]) - Channel_attributes = Channel(designation,material_grade) - source = str(Channel_attributes.source) - fu = str(Channel_attributes.fu) - fy = str(Channel_attributes.fy) - f_w = (Channel_attributes.flange_width) - f_t = (Channel_attributes.flange_thickness) - w_h = (Channel_attributes.depth) - w_t = (Channel_attributes.web_thickness) - flange_slope = float(Channel_attributes.flange_slope) - root_radius = str(Channel_attributes.root_radius) - toe_radius = str(Channel_attributes.toe_radius) - plate_thk = float(input_dictionary[KEY_PLATETHK][0]) - m_o_e = "200" - m_o_r = "76.9" - p_r = "0.3" - t_e = "12" - Type = str(Channel_attributes.type) - if section_profile == "Channels": - mass = str(round((Channel_attributes.mass), 2)) - area = str(round((Channel_attributes.area / 100), 2)) - C_y = str(round((Channel_attributes.Cy / 10), 2)) - mom_inertia_z = str(round((Channel_attributes.mom_inertia_z) / 10000, 2)) - mom_inertia_y = str(round((Channel_attributes.mom_inertia_y) / 10000, 2)) - rad_of_gy_z = str(round((Channel_attributes.rad_of_gy_z / 10), 2)) - rad_of_gy_y = str(round((Channel_attributes.rad_of_gy_y / 10), 2)) - elast_sec_mod_z = str(round((Channel_attributes.elast_sec_mod_z / 1000), 2)) - elast_sec_mod_y = str(round((Channel_attributes.elast_sec_mod_y / 1000), 2)) - plast_sec_mod_z = str(round((Channel_attributes.plast_sec_mod_z / 1000), 2)) - plast_sec_mod_y = str(round((Channel_attributes.plast_sec_mod_y / 1000), 2)) - if flange_slope != 90: - image = VALUES_IMG_TENSIONBOLTED_DF03[0] - else: - image = VALUES_IMG_TENSIONBOLTED_DF03[1] - It = str(round((Channel_attributes.It / 10000), 2)) - Iw = str(round((Channel_attributes.Iw / 1000000), 2)) - else: - Channel_attributes = BBChannel_Properties() - Channel_attributes.data(designation,material_grade) - mass = str(round(Channel_attributes.calc_Mass(f_w, f_t, w_h, w_t), 2)) - area = str(round(Channel_attributes.calc_Area(f_w, f_t, w_h, w_t), 2)) - C_y = "N/A" - mom_inertia_z = str(round(Channel_attributes.calc_MomentOfAreaZ(f_w, f_t, w_h, w_t,plate_thk), 2)) - mom_inertia_y = str(Channel_attributes.calc_MomentOfAreaY(f_w, f_t, w_h, w_t,plate_thk)) - rad_of_gy_z = str(Channel_attributes.calc_RogZ(f_w, f_t, w_h, w_t, plate_thk)) - rad_of_gy_y = str(Channel_attributes.calc_RogY(f_w, f_t, w_h, w_t, plate_thk)) - elast_sec_mod_z = str(Channel_attributes.calc_ElasticModulusZz(f_w, f_t, w_h, w_t,plate_thk)) - elast_sec_mod_y = str(Channel_attributes.calc_ElasticModulusZy(f_w, f_t, w_h, w_t,plate_thk)) - plast_sec_mod_z = str(Channel_attributes.calc_PlasticModulusZpz(f_w, f_t, w_h, w_t,plate_thk)) - plast_sec_mod_y = str(Channel_attributes.calc_PlasticModulusZpy(f_w, f_t, w_h, w_t,plate_thk)) - if flange_slope != 90: - image = VALUES_IMG_TENSIONBOLTED_DF03[2] - else: - image = VALUES_IMG_TENSIONBOLTED_DF03[3] - It = str(Channel_attributes.calc_TorsionConstantIt(f_w, f_t, w_h, w_t)) - Iw = "-" - - if KEY_SEC_MATERIAL in input_dictionary.keys(): - material_grade = input_dictionary[KEY_SEC_MATERIAL] - material_attributes = Material(material_grade) - fu = material_attributes.fu - fy = material_attributes.fy - - section = [] - - if input_dictionary: - designation_list = input_dictionary[KEY_SECSIZE] - else: - designation_list = [] - - t0 = (KEY_SECSIZE, KEY_DISP_DESIGNATION, TYPE_COMBOBOX, designation_list, designation) - section.append(t0) - - t1 = (KEY_SECSIZE_SELECTED, KEY_DISP_DESIGNATION, TYPE_TEXTBOX, None, designation) - section.append(t1) - - t1 = (KEY_SEC_PROFILE, KEY_DISP_SEC_PROFILE, TYPE_TEXTBOX, None, section_profile) - section.append(t1) - - t1 = (KEY_LOCATION, KEY_DISP_LOCATION, TYPE_TEXTBOX, None, l) - section.append(t1) - - t2 = (None, KEY_DISP_MECH_PROP, TYPE_TITLE, None, None) - section.append(t2) - - material = connectdb("Material", call_type="popup") - t34 = (KEY_SEC_MATERIAL, KEY_DISP_MATERIAL, TYPE_COMBOBOX, material, material_grade) - section.append(t34) - - t3 = (KEY_SEC_FU, KEY_DISP_FU, TYPE_TEXTBOX, None, fu) - section.append(t3) - - t4 = (KEY_SEC_FY, KEY_DISP_FY, TYPE_TEXTBOX, None, fy) - section.append(t4) - - t15 = ('Label_7', KEY_DISP_MOD_OF_ELAST, TYPE_TEXTBOX, None, m_o_e) - section.append(t15) - - t16 = ('Label_8', KEY_DISP_MOD_OF_RIGID, TYPE_TEXTBOX, None, m_o_r) - section.append(t16) - - t31 = ('Label_24', KEY_DISP_POISSON_RATIO, TYPE_TEXTBOX, None, p_r) - section.append(t31) - - t32 = ('Label_25', KEY_DISP_THERMAL_EXP, TYPE_TEXTBOX, None, t_e) - section.append(t32) - - t14 = ('Label_6', KEY_DISP_TYPE, TYPE_COMBOBOX, ['Rolled', 'Welded'], Type) - section.append(t14) - - t29 = (KEY_SOURCE, KEY_DISP_SOURCE, TYPE_TEXTBOX, None, source) - section.append(t29) - - t28 = (None, None, TYPE_BREAK, None, None) - section.append(t28) - - t5 = (None, KEY_DISP_DIMENSIONS, TYPE_TITLE, None, None) - section.append(t5) - - t6 = ('Label_1', KEY_DISP_FLANGE_W, TYPE_TEXTBOX, None, f_w) - section.append(t6) - - t7 = ('Label_2', KEY_DISP_FLANGE_T, TYPE_TEXTBOX, None, f_t) - section.append(t7) - - t8 = ('Label_3', KEY_DISP_DEPTH, TYPE_TEXTBOX, None, w_h) - section.append(t8) - - t22 = ('Label_13', KEY_DISP_WEB_T, TYPE_TEXTBOX, None, w_t) - section.append(t22) - - t23 = ('Label_14', KEY_DISP_FLANGE_S, TYPE_TEXTBOX, None, flange_slope) - section.append(t23) - - t11 = ('Label_4', KEY_DISP_ROOT_R, TYPE_TEXTBOX, None, root_radius) - section.append(t11) - - t12 = ('Label_5', KEY_DISP_TOE_R, TYPE_TEXTBOX, None, toe_radius) - section.append(t12) - - t12 = ('Label_0', KEY_DISP_DPPLATETHK01, TYPE_COMBOBOX, VALUES_PLATETHK_CUSTOMIZED, plate_thk) - section.append(t12) - - t17 = (None, KEY_DISP_SEC_PROP, TYPE_TITLE, None, None) - section.append(t17) - - t18 = ('Label_9', KEY_DISP_MASS, TYPE_TEXTBOX, None, mass) - section.append(t18) - - t19 = ('Label_10', KEY_DISP_AREA, TYPE_TEXTBOX, None, area) - section.append(t19) - - # t13 = (None, None, TYPE_BREAK, None, None) - # section.append(t13) - # - # - - # t18 = (None, None, TYPE_ENTER, None, None) - # section.append(t18) - # - # t18 = (None, None, TYPE_ENTER, None, None) - # section.append(t18) - - - - # t18 = (None, None, TYPE_ENTER, None, None) - # section.append(t18) - - # t17 = (None, KEY_DISP_SEC_PROP, TYPE_TITLE, None, None) - # section.append(t17) - - t20 = ('Label_17', KEY_DISP_Cy, TYPE_TEXTBOX, None, C_y) - section.append(t20) - - t20 = ('Label_11', KEY_DISP_MOA_IZ, TYPE_TEXTBOX, None, mom_inertia_z) - section.append(t20) - - t21 = ('Label_12', KEY_DISP_MOA_IY, TYPE_TEXTBOX, None, mom_inertia_y) - section.append(t21) - - t22 = ('Label_15', KEY_DISP_ROG_RZ, TYPE_TEXTBOX, None, rad_of_gy_z) - section.append(t22) - - t23 = ('Label_16', KEY_DISP_ROG_RY, TYPE_TEXTBOX, None, rad_of_gy_y) - section.append(t23) - - t28 = (None, None, TYPE_BREAK, None, None) - section.append(t28) - - t33 = (KEY_IMAGE, None, TYPE_IMAGE, None, image) - section.append(t33) - - t17 = (None, KEY_DISP_SEC_PROP, TYPE_TITLE, None, None) - section.append(t17) - - t24 = ('Label_19', KEY_DISP_EM_ZZ, TYPE_TEXTBOX, None, elast_sec_mod_z) - section.append(t24) - - t25 = ('Label_20', KEY_DISP_EM_ZY, TYPE_TEXTBOX, None, elast_sec_mod_y) - section.append(t25) - - t26 = ('Label_21', KEY_DISP_PM_ZPZ, TYPE_TEXTBOX, None, plast_sec_mod_z) - section.append(t26) - - t27 = ('Label_22', KEY_DISP_PM_ZPY, TYPE_TEXTBOX, None, plast_sec_mod_y) - section.append(t27) - - t27 = ('Label_26', KEY_DISP_It, TYPE_TEXTBOX, None, It) - section.append(t27) - - t27 = ('Label_27', KEY_DISP_Iw, TYPE_TEXTBOX, None, Iw) - section.append(t27) - - return section - - - def get_new_angle_section_properties(self): - - designation = self[0] - material_grade = self[1] - l = self[3][KEY_LOCATION] - section_profile = self[3][KEY_SEC_PROFILE] - plate_thk = float(self[2]) - Angle_attributes = Angle(designation, material_grade) - source = str(Angle_attributes.source) - fu = str(Angle_attributes.fu) - fy = str(Angle_attributes.fy) - a = (Angle_attributes.a) - b = (Angle_attributes.b) - thickness = (Angle_attributes.thickness) - root_radius = str(Angle_attributes.root_radius) - toe_radius = str(Angle_attributes.toe_radius) - Type = str(Angle_attributes.type) - m_o_e = "200" - m_o_r = "76.9" - p_r = "0.3" - t_e = "12" - if section_profile == 'Angles': - mass = str(round((Angle_attributes.mass),2)) - area = str(round((Angle_attributes.area/100),2)) - Cz = str(round((Angle_attributes.Cz/10),2)) - Cy = str(round((Angle_attributes.Cy/10),2)) - mom_inertia_z = str(round((Angle_attributes.mom_inertia_z)/10000,2)) - mom_inertia_y = str(round((Angle_attributes.mom_inertia_y)/10000,2)) - mom_inertia_u = str(round((Angle_attributes.mom_inertia_u)/10000,2)) - mom_inertia_v = str(round((Angle_attributes.mom_inertia_v)/10000,2)) - rad_of_gy_z = str(round((Angle_attributes.rad_of_gy_z/10),2)) - rad_of_gy_y = str(round((Angle_attributes.rad_of_gy_y/10),2)) - rad_of_gy_u = str(round((Angle_attributes.rad_of_gy_u/10),2)) - rad_of_gy_v = str(round((Angle_attributes.rad_of_gy_v/10),2)) - elast_sec_mod_z = str(round((Angle_attributes.elast_sec_mod_z/1000),2)) - elast_sec_mod_y = str(round((Angle_attributes.elast_sec_mod_y/1000),2)) - plast_sec_mod_z = str(round((Angle_attributes.plast_sec_mod_z/1000),2)) - plast_sec_mod_y = str(round((Angle_attributes.plast_sec_mod_y/1000),2)) - torsional_rigidity = str(round((Angle_attributes.It/10000),2)) - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[0] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[0] - else: - # Angle_attributes = Angle(designation, material_grade) - if section_profile == "Back to Back Angles": - print(section_profile, "hjcxhf") - Angle_attributes = BBAngle_Properties() - Angle_attributes.data(designation, material_grade) - if l == "Long Leg": - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[1] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[1] - Cz = str(Angle_attributes.calc_Cz(a, b, thickness, l)) - Cy = "N/A" - else: - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[2] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[2] - Cy = "N/A" - Cz = str(Angle_attributes.calc_Cz(a, b, thickness, l)) - mass = str(Angle_attributes.calc_Mass(a, b, thickness, l)) - area = str(Angle_attributes.calc_Area(a, b, thickness, l)) - - mom_inertia_z = str(Angle_attributes.calc_MomentOfAreaZ(a, b, thickness, l, plate_thk)) - mom_inertia_y = str(Angle_attributes.calc_MomentOfAreaY(a, b, thickness, l, plate_thk)) - mom_inertia_u = str(Angle_attributes.calc_MomentOfAreaY(a, b, thickness, l, plate_thk)) - mom_inertia_v = str(Angle_attributes.calc_MomentOfAreaZ(a, b, thickness, l, plate_thk)) - rad_of_gy_z = str(Angle_attributes.calc_RogZ(a, b, thickness, l, plate_thk)) - rad_of_gy_y = str(Angle_attributes.calc_RogY(a, b, thickness, l, plate_thk)) - rad_of_gy_u = str(Angle_attributes.calc_RogY(a, b, thickness, l, plate_thk)) - rad_of_gy_v = str(Angle_attributes.calc_RogZ(a, b, thickness, l, plate_thk)) - elast_sec_mod_z = str(Angle_attributes.calc_ElasticModulusZz(a, b, thickness, l, plate_thk)) - elast_sec_mod_y = str(Angle_attributes.calc_ElasticModulusZy(a, b, thickness, l, plate_thk)) - plast_sec_mod_z = str(Angle_attributes.calc_PlasticModulusZpz(a, b, thickness, l, plate_thk)) - plast_sec_mod_y = str(Angle_attributes.calc_PlasticModulusZpy(a, b, thickness, l, plate_thk)) - torsional_rigidity = str(Angle_attributes.calc_TorsionConstantIt(a, b, thickness, l)) - else: # "Star Angles": - Angle_attributes = SAngle_Properties() - Angle_attributes.data(designation, material_grade) - if l == "Long Leg": - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[3] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[3] - else: - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[4] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[4] - Cz = "N/A" - Cy = "N/A" - mass = str(Angle_attributes.calc_Mass(a, b, thickness, l)) - area = str(Angle_attributes.calc_Area(a, b,thickness, l)) - - mom_inertia_z = str(Angle_attributes.calc_MomentOfAreaZ(a, b, thickness, l, plate_thk)) - mom_inertia_y = str(Angle_attributes.calc_MomentOfAreaY(a, b, thickness, l, plate_thk)) - mom_inertia_u = str(Angle_attributes.calc_MomentOfAreaU(a, b, thickness, l, plate_thk)) - mom_inertia_v = str(Angle_attributes.calc_MomentOfAreaV(a, b, thickness, l, plate_thk)) - rad_of_gy_z = str(Angle_attributes.calc_RogZ(a, b, thickness, l, plate_thk)) - rad_of_gy_y = str(Angle_attributes.calc_RogY(a, b, thickness, l, plate_thk)) - rad_of_gy_u = str(Angle_attributes.calc_RogU(a, b, thickness, l, plate_thk)) - rad_of_gy_v = str(Angle_attributes.calc_RogV(a, b, thickness, l, plate_thk)) - elast_sec_mod_z = str(Angle_attributes.calc_ElasticModulusZz(a, b, thickness, l, plate_thk)) - elast_sec_mod_y = str(Angle_attributes.calc_ElasticModulusZy(a, b, thickness, l, plate_thk)) - plast_sec_mod_z = str(Angle_attributes.calc_PlasticModulusZpz(a, b, thickness, l, plate_thk)) - plast_sec_mod_y = str(Angle_attributes.calc_PlasticModulusZpy(a, b, thickness, l, plate_thk)) - torsional_rigidity = str(Angle_attributes.calc_TorsionConstantIt(a, b, thickness, l)) - - d = { - KEY_SECSIZE_SELECTED:designation, - KEY_SEC_MATERIAL: material_grade, - KEY_SEC_FY:fy, - KEY_SEC_FU:fu, - 'Label_1': a, - 'Label_2': b, - 'Label_3':thickness, - 'Label_4':root_radius, - 'Label_5':toe_radius, - 'Label_0': plate_thk, - 'Label_6':Type, - 'Label_7': Cz, - 'Label_8': Cy, - 'Label_9':mass, - 'Label_10':area, - 'Label_11':mom_inertia_z, - 'Label_12':mom_inertia_y, - 'Label_13':mom_inertia_u, - 'Label_14':mom_inertia_v, - 'Label_15':rad_of_gy_z, - 'Label_16':rad_of_gy_y, - 'Label_17':rad_of_gy_u, - 'Label_18':rad_of_gy_v, - 'Label_19':elast_sec_mod_z, - 'Label_20':elast_sec_mod_y, - 'Label_21':plast_sec_mod_z, - 'Label_22':plast_sec_mod_y, - 'Label_23':torsional_rigidity, - 'Label_24':source - ,KEY_IMAGE:image - } - return d - - def get_new_channel_section_properties(self): - designation = self[0] - material_grade = self[1] - # sl = self[2] - l = self[3][KEY_LOCATION] - section_profile = self[3][KEY_SEC_PROFILE] - plate_thk = float(self[2]) - Channel_attributes = Channel(designation, material_grade) - source = str(Channel_attributes.source) - Type = str(Channel_attributes.type) - fu = str(Channel_attributes.fu) - fy = str(Channel_attributes.fy) - f_w = (Channel_attributes.flange_width) - f_t = (Channel_attributes.flange_thickness) - w_h = (Channel_attributes.depth) - w_t = (Channel_attributes.web_thickness) - flange_slope = (Channel_attributes.flange_slope) - root_radius = str(Channel_attributes.root_radius) - toe_radius = str(Channel_attributes.toe_radius) - m_o_e = "200" - m_o_r = "76.9" - p_r = "0.3" - t_e = "12" - if section_profile == "Channels": - mass = str(round((Channel_attributes.mass), 2)) - area = str(round((Channel_attributes.area / 100), 2)) - C_y = str(round((Channel_attributes.Cy / 10), 2)) - mom_inertia_z = str(round((Channel_attributes.mom_inertia_z) / 10000, 2)) - mom_inertia_y = str(round((Channel_attributes.mom_inertia_y) / 10000, 2)) - rad_of_gy_z = str(round((Channel_attributes.rad_of_gy_z / 10), 2)) - rad_of_gy_y = str(round((Channel_attributes.rad_of_gy_y / 10), 2)) - elast_sec_mod_z = str(round((Channel_attributes.elast_sec_mod_z / 1000), 2)) - elast_sec_mod_y = str(round((Channel_attributes.elast_sec_mod_y / 1000), 2)) - plast_sec_mod_z = str(round((Channel_attributes.plast_sec_mod_z / 1000), 2)) - plast_sec_mod_y = str(round((Channel_attributes.plast_sec_mod_y / 1000), 2)) - if flange_slope != 90: - image = VALUES_IMG_TENSIONBOLTED_DF03[0] - else: - image = VALUES_IMG_TENSIONBOLTED_DF03[1] - # if Channel_attributes.It = "N/A": - # It = str(Channel_attributes.It ) - # else: - It = str(round((Channel_attributes.It / 10000), 2)) - Iw = str(round((Channel_attributes.Iw/ 1000000), 2)) - else: - Channel_attributes = BBChannel_Properties() - Channel_attributes.data(designation,material_grade) - mass = str(Channel_attributes.calc_Mass(f_w, f_t, w_h, w_t)) - area = str(Channel_attributes.calc_Area(f_w, f_t, w_h, w_t)) - C_y = "N/A" - mom_inertia_z = str(Channel_attributes.calc_MomentOfAreaZ(f_w, f_t, w_h, w_t,plate_thk)) - mom_inertia_y = str(Channel_attributes.calc_MomentOfAreaY(f_w, f_t, w_h, w_t,plate_thk)) - rad_of_gy_z = str(Channel_attributes.calc_RogZ(f_w, f_t, w_h, w_t,plate_thk)) - rad_of_gy_y = str(Channel_attributes.calc_RogY(f_w, f_t, w_h, w_t,plate_thk)) - elast_sec_mod_z = str(Channel_attributes.calc_ElasticModulusZz(f_w, f_t, w_h, w_t,plate_thk)) - elast_sec_mod_y = str(Channel_attributes.calc_ElasticModulusZy(f_w, f_t, w_h, w_t,plate_thk)) - plast_sec_mod_z = str(Channel_attributes.calc_PlasticModulusZpz(f_w, f_t, w_h, w_t,plate_thk)) - plast_sec_mod_y = str(Channel_attributes.calc_PlasticModulusZpy(f_w, f_t, w_h, w_t,plate_thk)) - if flange_slope != 90: - image = VALUES_IMG_TENSIONBOLTED_DF03[2] - else: - image = VALUES_IMG_TENSIONBOLTED_DF03[3] - It = str(Channel_attributes.calc_TorsionConstantIt(f_w, f_t, w_h, w_t)) - Iw = "-" - - d = { - KEY_SECSIZE_SELECTED: designation, - KEY_SEC_MATERIAL: material_grade, - KEY_SEC_FY: fy, - KEY_SEC_FU: fu, - 'Label_1': str(f_w), - 'Label_2': str(f_t), - 'Label_3': str(w_h), - 'Label_13': str(w_t), - 'Label_14': str(flange_slope), - 'Label_5': str(toe_radius), - 'Label_6': str(Type), - 'Label_4': str(root_radius), - 'Label_0': str(plate_thk), - 'Label_9': str(mass), - 'Label_10': str(area), - 'Label_11': str(mom_inertia_z), - 'Label_12': str(mom_inertia_y), - 'Label_15': str(rad_of_gy_z), - 'Label_16': str(rad_of_gy_y), - 'Label_17': str(C_y), - 'Label_19': str(elast_sec_mod_z), - 'Label_20': str(elast_sec_mod_y), - 'Label_21': str(plast_sec_mod_z), - 'Label_22': str(plast_sec_mod_y), - 'Label_23': str(source), - 'Label_26': str(It), - 'Label_27': str(Iw), - KEY_IMAGE: image - } - return d - - def get_Angle_sec_properties(self): - if '' in self: - mass = '' - area = '' - Cz = '' - Cy = '' - moa_z = '' - moa_y = '' - moa_u = '' - moa_v = '' - rog_z = '' - rog_y = '' - rog_u = '' - rog_v = '' - em_z = '' - em_y = '' - pm_z = '' - pm_y = '' - I_t = '' - image = '' - else: - a = float(self[0]) - b = float(self[1]) - t = float(self[2]) - # plate_thk = float(self[3][KEY_PLATETHK][0]) - plate_thk = float(self[3]) - - l = self[4][KEY_LOCATION] - p = self[4][KEY_SEC_PROFILE] - - if p == "Angles": - sec_prop = Single_Angle_Properties() - mass = sec_prop.calc_Mass(a, b, t, l) - area = sec_prop.calc_Area(a, b, t, l) - Cz = sec_prop.calc_Cz(a, b, t, l) - Cy = sec_prop.calc_Cy(a, b, t, l) - moa_z = sec_prop.calc_MomentOfAreaZ(a, b, t, l) - moa_y = sec_prop.calc_MomentOfAreaY(a, b, t, l) - # a = sec_prop.calc_MomentOfAreaYZ(a, b, t, l) - moa_u = sec_prop.calc_MomentOfAreaU(a, b, t, l) - moa_v = sec_prop.calc_MomentOfAreaV(a, b, t, l) - rog_z = sec_prop.calc_RogZ(a, b, t, l) - rog_y = sec_prop.calc_RogY(a, b, t, l) - rog_u = sec_prop.calc_RogU(a, b, t, l) - rog_v = sec_prop.calc_RogV(a, b, t, l) - em_z = sec_prop.calc_ElasticModulusZz(a, b, t, l) - em_y = sec_prop.calc_ElasticModulusZy(a, b, t, l) - pm_z = sec_prop.calc_PlasticModulusZpz(a, b, t, l) - pm_y = sec_prop.calc_PlasticModulusZpy(a, b, t, l) - I_t = sec_prop.calc_TorsionConstantIt(a, b, t, l) - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[0] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[0] - - elif p == "Back to Back Angles": - sec_prop = BBAngle_Properties() - mass = sec_prop.calc_Mass(a, b, t, l) - area = sec_prop.calc_Area(a, b, t, l) - if l == "Long Leg": - Cz = sec_prop.calc_Cz(a, b, t, l) - Cy = "N/A" - else: - Cz = sec_prop.calc_Cz(a, b, t, l) - Cy = "N/A" - moa_z = sec_prop.calc_MomentOfAreaZ(a, b, t, l,plate_thk) - moa_y = sec_prop.calc_MomentOfAreaY(a, b, t, l,plate_thk) - moa_u = sec_prop.calc_MomentOfAreaY(a, b, t, l,plate_thk) - moa_v = sec_prop.calc_MomentOfAreaZ(a, b, t, l,plate_thk) - rog_z = sec_prop.calc_RogZ(a, b, t, l, plate_thk) - rog_y = sec_prop.calc_RogY(a, b, t, l, plate_thk) - rog_u = sec_prop.calc_RogY(a, b, t, l, plate_thk) - rog_v = sec_prop.calc_RogZ(a, b, t, l, plate_thk) - em_z = sec_prop.calc_ElasticModulusZz(a, b, t, l,plate_thk) - em_y = sec_prop.calc_ElasticModulusZy(a, b, t, l,plate_thk) - pm_z = sec_prop.calc_PlasticModulusZpz(a, b, t, l,plate_thk) - pm_y = sec_prop.calc_PlasticModulusZpy(a, b, t, l,plate_thk) - I_t = sec_prop.calc_TorsionConstantIt(a, b, t, l) - if l == "Long Leg": - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[1] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[1] - else: - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[2] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[2] - else: - sec_prop = SAngle_Properties() - mass = sec_prop.calc_Mass(a, b, t, l) - area = sec_prop.calc_Area(a, b, t, l) - Cz = "N/A" - Cy = "N/A" - moa_z = sec_prop.calc_MomentOfAreaZ(a, b, t, l,plate_thk) - moa_y = sec_prop.calc_MomentOfAreaY(a, b, t, l,plate_thk) - moa_u = sec_prop.calc_MomentOfAreaU(a, b, t, l,plate_thk) - moa_v = sec_prop.calc_MomentOfAreaV(a, b, t, l,plate_thk) - rog_z = sec_prop.calc_RogZ(a, b, t, l,plate_thk) - rog_y = sec_prop.calc_RogY(a, b, t, l,plate_thk) - rog_u = sec_prop.calc_RogU(a, b, t, l,plate_thk) - rog_v = sec_prop.calc_RogV(a, b, t, l,plate_thk) - em_z = sec_prop.calc_ElasticModulusZz(a, b, t, l,plate_thk) - em_y = sec_prop.calc_ElasticModulusZy(a, b, t, l,plate_thk) - pm_z = sec_prop.calc_PlasticModulusZpz(a, b, t, l,plate_thk) - pm_y = sec_prop.calc_PlasticModulusZpy(a, b, t, l,plate_thk) - I_t = sec_prop.calc_TorsionConstantIt(a, b, t, l) - if l == "Long Leg": - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[3] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[3] - else: - if a == b: - image = VALUES_IMG_TENSIONBOLTED_DF01[4] - else: - image = VALUES_IMG_TENSIONBOLTED_DF02[4] - - d = {'Label_9': str(mass), - 'Label_10': str(area), - 'Label_7': str(Cz), - 'Label_8': str(Cy), - 'Label_11': str(moa_z), - 'Label_12': str(moa_y), - 'Label_13': str(moa_u), - 'Label_14': str(moa_v), - 'Label_15': str(rog_z), - 'Label_16': str(rog_y), - 'Label_17': str(rog_u), - 'Label_18': str(rog_v), - 'Label_19': str(em_z), - 'Label_20': str(em_y), - 'Label_21': str(pm_z), - 'Label_22': str(pm_y), - 'Label_23': str(I_t) - ,KEY_IMAGE: image - } - - return d - - def get_Channel_sec_properties(self): - - if '' in self: - mass = '' - area = '' - C_y = '' - moa_z = '' - moa_y = '' - - rog_z = '' - rog_y = '' - - em_z = '' - em_y = '' - pm_z = '' - pm_y = '' - - It = '' - Iw = '' - image ='' - - else: - f_w = float(self[0]) - f_t = float(self[1]) - w_h = float(self[2]) - w_t = float(self[3]) - sl = float(self[4]) - plate_thk = float(self[5][KEY_PLATETHK][0]) - l = self[5][KEY_LOCATION] - p = self[5][KEY_SEC_PROFILE] - - if p =="Channels": - sec_prop = Single_Channel_Properties() - mass = sec_prop.calc_Mass(f_w, f_t, w_h, w_t) - area = sec_prop.calc_Area(f_w, f_t, w_h, w_t) - C_y = sec_prop.calc_C_y(f_w, f_t, w_h, w_t) - moa_z = sec_prop.calc_MomentOfAreaZ(f_w, f_t, w_h, w_t) - moa_y = sec_prop.calc_MomentOfAreaY(f_w, f_t, w_h, w_t) - - rog_z = sec_prop.calc_RogZ(f_w, f_t, w_h, w_t) - rog_y = sec_prop.calc_RogY(f_w, f_t, w_h, w_t) - - em_z = sec_prop.calc_ElasticModulusZz(f_w, f_t, w_h, w_t) - em_y = sec_prop.calc_ElasticModulusZy(f_w, f_t, w_h, w_t) - pm_z = sec_prop.calc_PlasticModulusZpz(f_w, f_t, w_h, w_t) - pm_y = sec_prop.calc_PlasticModulusZpy(f_w, f_t, w_h, w_t) - It = sec_prop.calc_TorsionConstantIt(f_w, f_t, w_h, w_t) - Iw = 0 - if sl != 90: - image = VALUES_IMG_TENSIONBOLTED_DF03[0] - else: - image = VALUES_IMG_TENSIONBOLTED_DF03[1] - - else: - sec_prop = BBChannel_Properties() - mass = sec_prop.calc_Mass(f_w, f_t, w_h, w_t) - area = sec_prop.calc_Area(f_w, f_t, w_h, w_t) - C_y = "N/A" - moa_z = sec_prop.calc_MomentOfAreaZ(f_w, f_t, w_h, w_t, plate_thk) - moa_y = sec_prop.calc_MomentOfAreaY(f_w, f_t, w_h, w_t, plate_thk) - - rog_z = sec_prop.calc_RogZ(f_w, f_t, w_h, w_t,plate_thk) - rog_y = sec_prop.calc_RogY(f_w, f_t, w_h, w_t,plate_thk) - - em_z = sec_prop.calc_ElasticModulusZz(f_w, f_t, w_h, w_t, plate_thk) - em_y = sec_prop.calc_ElasticModulusZy(f_w, f_t, w_h, w_t, plate_thk) - pm_z = sec_prop.calc_PlasticModulusZpz(f_w, f_t, w_h, w_t, plate_thk) - pm_y = sec_prop.calc_PlasticModulusZpy(f_w, f_t, w_h, w_t, plate_thk) - - It = sec_prop.calc_TorsionConstantIt(f_w, f_t, w_h, w_t) - Iw = "-" - if sl != 90: - image = VALUES_IMG_TENSIONBOLTED_DF03[2] - else: - image = VALUES_IMG_TENSIONBOLTED_DF03[3] - - - d = {'Label_9': str(mass), - 'Label_10': str(area), - 'Label_11': str(moa_z), - 'Label_12': str(moa_y), - 'Label_15': str(rog_z), - 'Label_16': str(rog_y), - 'Label_17': str(C_y), - 'Label_19': str(em_z), - 'Label_20': str(em_y), - 'Label_21': str(pm_z), - 'Label_22': str(pm_y), - 'Label_26': str(It), - 'Label_27': str(Iw), - KEY_IMAGE : image - } - - return d - - def tab_section(self, input_dictionary): - - "In design preference, it shows other properties of section used " - - if not input_dictionary or input_dictionary[KEY_SECSIZE] == 'Select Section' or \ - input_dictionary[KEY_MATERIAL] == 'Select Material': - designation = '' - material_grade = '' - source = 'Custom' - fu = '' - fy = '' - depth = '' - flange_width = '' - flange_thickness = '' - web_thickness = '' - flange_slope = '' - root_radius = '' - toe_radius = '' - m_o_e = "200" - m_o_r = "76.9" - p_r = "0.3" - t_e = "12" - mass = '' - area = '' - mom_inertia_z = '' - mom_inertia_y = '' - rad_of_gy_z = '' - rad_of_gy_y = '' - elast_sec_mod_z = '' - elast_sec_mod_y = '' - plast_sec_mod_z = '' - plast_sec_mod_y = '' - torsion_const = '' - warping_const = '' - - image = '' - - - else: - designation = str(input_dictionary[KEY_SECSIZE][0]) - material_grade = str(input_dictionary[KEY_MATERIAL]) - m_o_e = "200" - m_o_r = "76.9" - p_r = "0.3" - t_e = "12" - image = VALUES_IMG_BEAM[0] - if designation in connectdb("RHS", call_type="popup"): - RHS_sec_attributes = RHS(designation, material_grade) - fu = str(RHS_sec_attributes.fu) - fy = str(RHS_sec_attributes.fy) - source = str(RHS_sec_attributes.source) - depth = str(RHS_sec_attributes.depth) - width = str(RHS_sec_attributes.flange_width) - thickness = str(RHS_sec_attributes.flange_thickness) - mass = str(RHS_sec_attributes.mass) - area = str(round((RHS_sec_attributes.area / 10 ** 2), 2)) - mom_inertia_z = str(round((RHS_sec_attributes.mom_inertia_z / 10 ** 4), 2)) - mom_inertia_y = str(round((RHS_sec_attributes.mom_inertia_y / 10 ** 4), 2)) - rad_of_gy_z = str(round((RHS_sec_attributes.rad_of_gy_z / 10), 2)) - rad_of_gy_y = str(round((RHS_sec_attributes.rad_of_gy_y / 10), 2)) - elast_sec_mod_z = str(round((RHS_sec_attributes.elast_sec_mod_z / 10 ** 3), 2)) - elast_sec_mod_y = str(round((RHS_sec_attributes.elast_sec_mod_y / 10 ** 3), 2)) - plast_sec_mod_z = str(round((RHS_sec_attributes.plast_sec_mod_z / 10 ** 3), 2)) - plast_sec_mod_y = str(round((RHS_sec_attributes.plast_sec_mod_y / 10 ** 3), 2)) - image = VALUES_IMG_HOLLOWSECTION[1] - elif designation in connectdb("SHS", call_type="popup"): - SHS_sec_attributes = SHS(designation, material_grade) - fu = str(SHS_sec_attributes.fu) - fy = str(SHS_sec_attributes.fy) - source = str(SHS_sec_attributes.source) - depth = str(SHS_sec_attributes.depth) - width = str(SHS_sec_attributes.flange_width) - thickness = str(SHS_sec_attributes.flange_thickness) - mass = str(SHS_sec_attributes.mass) - area = str(round((SHS_sec_attributes.area / 10 ** 2), 2)) - mom_inertia_z = str(round((SHS_sec_attributes.mom_inertia_z / 10 ** 4), 2)) - mom_inertia_y = str(round((SHS_sec_attributes.mom_inertia_y / 10 ** 4), 2)) - rad_of_gy_z = str(round((SHS_sec_attributes.rad_of_gy_z / 10), 2)) - rad_of_gy_y = str(round((SHS_sec_attributes.rad_of_gy_y / 10), 2)) - elast_sec_mod_z = str(round((SHS_sec_attributes.elast_sec_mod_z / 10 ** 3), 2)) - elast_sec_mod_y = str(round((SHS_sec_attributes.elast_sec_mod_y / 10 ** 3), 2)) - plast_sec_mod_z = str(round((SHS_sec_attributes.plast_sec_mod_z / 10 ** 3), 2)) - plast_sec_mod_y = str(round((SHS_sec_attributes.plast_sec_mod_y / 10 ** 3), 2)) - image = VALUES_IMG_HOLLOWSECTION[0] - elif designation in connectdb("CHS", call_type="popup"): - CHS_sec_attributes = CHS(designation, material_grade) - fu = str(CHS_sec_attributes.fu) - fy = str(CHS_sec_attributes.fy) - source = str(CHS_sec_attributes.source) - nominal_bore = str(CHS_sec_attributes.nominal_bore) - out_diameter = str(CHS_sec_attributes.out_diameter) - thickness = str(CHS_sec_attributes.flange_thickness) - mass = str(CHS_sec_attributes.mass) - area = str(round((CHS_sec_attributes.area / 10 ** 2), 2)) - internal_vol = str(CHS_sec_attributes.internal_vol) - mom_inertia = str(CHS_sec_attributes.mom_inertia) - rad_of_gy = str(round((CHS_sec_attributes.rad_of_gy / 10), 2)) - elast_sec_mod = str(round((CHS_sec_attributes.elast_sec_mod / 10 ** 3), 2)) - image = VALUES_IMG_HOLLOWSECTION[2] - else: - I_sec_attributes = ISection(designation) - table = "Beams" if designation in connectdb("Beams", "popup") else "Columns" - I_sec_attributes.connect_to_database_update_other_attributes(table, designation, material_grade) - source = str(I_sec_attributes.source) - fu = str(I_sec_attributes.fu) - fy = str(I_sec_attributes.fy) - depth = str(I_sec_attributes.depth) - flange_width = str(I_sec_attributes.flange_width) - flange_thickness = str(I_sec_attributes.flange_thickness) - web_thickness = str(I_sec_attributes.web_thickness) - flange_slope = float(I_sec_attributes.flange_slope) - root_radius = str(I_sec_attributes.root_radius) - toe_radius = str(I_sec_attributes.toe_radius) - mass = str(I_sec_attributes.mass) - area = str(round((I_sec_attributes.area / 10 ** 2), 2)) - mom_inertia_z = str(round((I_sec_attributes.mom_inertia_z / 10 ** 4), 2)) - mom_inertia_y = str(round((I_sec_attributes.mom_inertia_y / 10 ** 4), 2)) - rad_of_gy_z = str(round((I_sec_attributes.rad_of_gy_z / 10), 2)) - rad_of_gy_y = str(round((I_sec_attributes.rad_of_gy_y / 10), 2)) - elast_sec_mod_z = str(round((I_sec_attributes.elast_sec_mod_z / 10 ** 3), 2)) - elast_sec_mod_y = str(round((I_sec_attributes.elast_sec_mod_y / 10 ** 3), 2)) - plast_sec_mod_z = str(round((I_sec_attributes.plast_sec_mod_z / 10 ** 3), 2)) - plast_sec_mod_y = str(round((I_sec_attributes.plast_sec_mod_y / 10 ** 3), 2)) - torsion_const = str(round((I_sec_attributes.It / 10 ** 4), 2)) - warping_const = str(round((I_sec_attributes.Iw / 10 ** 6), 2)) - if flange_slope != 90: - image = VALUES_IMG_BEAM[0] - else: - image = VALUES_IMG_BEAM[1] - - if KEY_SEC_MATERIAL in input_dictionary.keys(): - material_grade = input_dictionary[KEY_SEC_MATERIAL] - material_attributes = Material(material_grade) - fu = material_attributes.fu - fy = material_attributes.fy - - section = [] - - if input_dictionary: - designation_list = input_dictionary[KEY_SECSIZE] - else: - designation_list = [] - - t0 = (KEY_SECSIZE, KEY_DISP_DESIGNATION, TYPE_COMBOBOX, designation_list, designation) - section.append(t0) - - t1 = (KEY_SECSIZE_SELECTED, KEY_DISP_DESIGNATION, TYPE_TEXTBOX, None, designation) - section.append(t1) - - t2 = (None, KEY_DISP_MECH_PROP, TYPE_TITLE, None, None) - section.append(t2) - - material = connectdb("Material", call_type="popup") - t34 = (KEY_SEC_MATERIAL, KEY_DISP_MATERIAL, TYPE_COMBOBOX, material, material_grade) - section.append(t34) - - t3 = (KEY_SEC_FU, KEY_DISP_FU, TYPE_TEXTBOX, None, fu) - section.append(t3) - - t4 = (KEY_SEC_FY, KEY_DISP_FY, TYPE_TEXTBOX, None, fy) - section.append(t4) - - t15 = ('Label_9', KEY_DISP_MOD_OF_ELAST, TYPE_TEXTBOX, None, m_o_e) - section.append(t15) - - t16 = ('Label_10', KEY_DISP_MOD_OF_RIGID, TYPE_TEXTBOX, None, m_o_r) - section.append(t16) - - t31 = ('Label_24', KEY_DISP_POISSON_RATIO, TYPE_TEXTBOX, None, p_r) - section.append(t31) - - t32 = ('Label_23', KEY_DISP_THERMAL_EXP, TYPE_TEXTBOX, None, t_e) - section.append(t32) - - t14 = ('Label_8', KEY_DISP_TYPE, TYPE_COMBOBOX, ['Rolled', 'Welded'], 'Rolled') - section.append(t14) - - t29 = (KEY_SOURCE, KEY_DISP_SOURCE, TYPE_TEXTBOX, None, source) - section.append(t29) - - t13 = (None, None, TYPE_BREAK, None, None) - section.append(t13) - - t5 = (None, KEY_DISP_DIMENSIONS, TYPE_TITLE, None, None) - section.append(t5) - - if designation in connectdb("RHS", call_type="popup") or designation in connectdb("SHS", call_type="popup"): - - t6 = ('Label_HS_1', KEY_DISP_DEPTH, TYPE_TEXTBOX, None, depth) - section.append(t6) - - t7 = ('Label_HS_2', KEY_DISP_WIDTH, TYPE_TEXTBOX, None, width) - section.append(t7) - - t8 = ('Label_HS_3', KEY_DISP_THICKNESS, TYPE_TEXTBOX, None, thickness) - section.append(t8) - - elif designation in connectdb("CHS", call_type="popup"): - - t6 = ('Label_CHS_1', KEY_DISP_NB, TYPE_TEXTBOX, None, nominal_bore) - section.append(t6) - - t7 = ('Label_CHS_2', KEY_DISP_OD, TYPE_TEXTBOX, None, out_diameter) - section.append(t7) - - t8 = ('Label_CHS_3', KEY_DISP_THICKNESS, TYPE_TEXTBOX, None, thickness) - section.append(t8) - - else: - - t6 = ('Label_1', KEY_DISP_DEPTH, TYPE_TEXTBOX, None, depth) - section.append(t6) - - t7 = ('Label_2', KEY_DISP_FLANGE_W, TYPE_TEXTBOX, None, flange_width) - section.append(t7) - - t8 = ('Label_3', KEY_DISP_FLANGE_T, TYPE_TEXTBOX, None, flange_thickness) - section.append(t8) - - t9 = ('Label_4', KEY_DISP_WEB_T, TYPE_TEXTBOX, None, web_thickness) - section.append(t9) - - t10 = ('Label_5', KEY_DISP_FLANGE_S, TYPE_TEXTBOX, None, flange_slope) - section.append(t10) - - t11 = ('Label_6', KEY_DISP_ROOT_R, TYPE_TEXTBOX, None, root_radius) - section.append(t11) - - t12 = ('Label_7', KEY_DISP_TOE_R, TYPE_TEXTBOX, None, toe_radius) - section.append(t12) - - t17 = (None, KEY_DISP_SEC_PROP, TYPE_TITLE, None, None) - section.append(t17) - - if designation in connectdb("RHS", call_type="popup") or designation in connectdb("SHS", call_type="popup"): - - t18 = ('Label_HS_11', KEY_DISP_MASS, TYPE_TEXTBOX, None, mass) - section.append(t18) - - t19 = ('Label_HS_12', KEY_DISP_AREA, TYPE_TEXTBOX, None, area) - section.append(t19) - - t20 = ('Label_HS_13', KEY_DISP_MOA_IZ, TYPE_TEXTBOX, None, mom_inertia_z) - section.append(t20) - - t21 = ('Label_HS_14', KEY_DISP_MOA_IY, TYPE_TEXTBOX, None, mom_inertia_y) - section.append(t21) - - t22 = ('Label_HS_15', KEY_DISP_ROG_RZ, TYPE_TEXTBOX, None, rad_of_gy_z) - section.append(t22) - - t23 = ('Label_HS_16', KEY_DISP_ROG_RY, TYPE_TEXTBOX, None, rad_of_gy_y) - section.append(t23) - - t24 = ('Label_HS_17', KEY_DISP_EM_ZZ, TYPE_TEXTBOX, None, elast_sec_mod_z) - section.append(t24) - - t25 = ('Label_HS_18', KEY_DISP_EM_ZY, TYPE_TEXTBOX, None, elast_sec_mod_y) - section.append(t25) - - t28 = (None, None, TYPE_BREAK, None, None) - section.append(t28) - - t33 = (KEY_IMAGE, None, TYPE_IMAGE, None, image) - section.append(t33) - - t17 = (None, KEY_DISP_SEC_PROP, TYPE_TITLE, None, None) - section.append(t17) - - t26 = ('Label_HS_19', KEY_DISP_PM_ZPZ, TYPE_TEXTBOX, None, plast_sec_mod_z) - section.append(t26) - - t27 = ('Label_HS_20', KEY_DISP_PM_ZPY, TYPE_TEXTBOX, None, plast_sec_mod_y) - section.append(t27) - - elif designation in connectdb("CHS", call_type="popup"): - - t18 = ('Label_CHS_11', KEY_DISP_MASS, TYPE_TEXTBOX, None, mass) - section.append(t18) - - t19 = ('Label_CHS_12', KEY_DISP_AREA, TYPE_TEXTBOX, None, area) - section.append(t19) - - t20 = ('Label_CHS_13', KEY_DISP_IV, TYPE_TEXTBOX, None, internal_vol) - section.append(t20) - - t21 = ('Label_HS_14', KEY_DISP_MOA, TYPE_TEXTBOX, None, mom_inertia) - section.append(t21) - - t23 = ('Label_HS_15', KEY_DISP_ROG, TYPE_TEXTBOX, None, rad_of_gy) - section.append(t23) - - t24 = ('Label_HS_16', KEY_DISP_SM, TYPE_TEXTBOX, None, elast_sec_mod) - section.append(t24) - - t28 = (None, None, TYPE_BREAK, None, None) - section.append(t28) - - t33 = (KEY_IMAGE, None, TYPE_IMAGE, None, image) - section.append(t33) - - else: - - t18 = ('Label_11', KEY_DISP_MASS, TYPE_TEXTBOX, None, mass) - section.append(t18) - - t19 = ('Label_12', KEY_DISP_AREA, TYPE_TEXTBOX, None, area) - section.append(t19) - - t20 = ('Label_13', KEY_DISP_MOA_IZ, TYPE_TEXTBOX, None, mom_inertia_z) - section.append(t20) - - t21 = ('Label_14', KEY_DISP_MOA_IY, TYPE_TEXTBOX, None, mom_inertia_y) - section.append(t21) - - t22 = ('Label_15', KEY_DISP_ROG_RZ, TYPE_TEXTBOX, None, rad_of_gy_z) - section.append(t22) - - t23 = ('Label_16', KEY_DISP_ROG_RY, TYPE_TEXTBOX, None, rad_of_gy_y) - section.append(t23) - - t24 = ('Label_17', KEY_DISP_EM_ZZ, TYPE_TEXTBOX, None, elast_sec_mod_z) - section.append(t24) - - t25 = ('Label_18', KEY_DISP_EM_ZY, TYPE_TEXTBOX, None, elast_sec_mod_y) - section.append(t25) - - t28 = (None, None, TYPE_BREAK, None, None) - section.append(t28) - - t33 = (KEY_IMAGE, None, TYPE_IMAGE, None, image) - section.append(t33) - - t17 = (None, KEY_DISP_SEC_PROP, TYPE_TITLE, None, None) - section.append(t17) - - t26 = ('Label_19', KEY_DISP_PM_ZPZ, TYPE_TEXTBOX, None, plast_sec_mod_z) - section.append(t26) - - t27 = ('Label_20', KEY_DISP_PM_ZPY, TYPE_TEXTBOX, None, plast_sec_mod_y) - section.append(t27) - - t26 = ('Label_21', KEY_DISP_It, TYPE_TEXTBOX, None, torsion_const) - section.append(t26) - - t27 = ('Label_22', KEY_DISP_Iw, TYPE_TEXTBOX, None, warping_const) - section.append(t27) - - return section - - - def get_fu_fy_I_section(self): - material_grade = self[0] - designation = self[1][KEY_SECSIZE] - - fu = '' - fy = '' - if material_grade != "Select Material" and designation != "Select Section": - table = "Beams" if designation in connectdb("Beams", "popup") else "Columns" - I_sec_attributes = ISection(designation) - I_sec_attributes.connect_to_database_update_other_attributes(table, designation, material_grade) - fu = str(I_sec_attributes.fu) - fy = str(I_sec_attributes.fy) - else: - pass - - d = {KEY_SUPTNGSEC_FU: fu, - KEY_SUPTNGSEC_FY: fy, - KEY_SUPTDSEC_FU: fu, - KEY_SUPTDSEC_FY: fy, - KEY_SEC_FU: fu, - KEY_SEC_FY: fy} - - return d - - def get_fu_fy_section(self): - material_grade = self[0] - designation = self[2][KEY_SECSIZE_SELECTED] - # designation = self[1][KEY_SECSIZE][0] - profile = self[1][KEY_SEC_PROFILE] - - if material_grade != "Select Material" and designation != "": - if profile in ['Angles', 'Back to Back Angles', 'Star Angles']: - Angle_sec_attributes = Angle(designation,material_grade) - Angle_sec_attributes.connect_to_database_update_other_attributes_angles(designation, material_grade) - fu = str(Angle_sec_attributes.fu) - fy = str(Angle_sec_attributes.fy) - elif profile in ['Channels', 'Back to Back Channels']: - Channel_Attributes = Channel(designation,material_grade) - Channel_Attributes.connect_to_database_update_other_attributes_channels(designation, material_grade) - fu = str(Channel_Attributes.fu) - fy = str(Channel_Attributes.fy) - elif profile in ['Beams']: - table = "Beams" - I_sec_attributes = ISection(designation) - I_sec_attributes.connect_to_database_update_other_attributes(table, designation, material_grade) - fu = str(I_sec_attributes.fu) - fy = str(I_sec_attributes.fy) - else: - table = "Columns" - I_sec_attributes = ISection(designation) - I_sec_attributes.connect_to_database_update_other_attributes(table, designation, material_grade) - fu = str(I_sec_attributes.fu) - fy = str(I_sec_attributes.fy) - else: - fu = '' - fy = '' - - d = {KEY_SEC_MATERIAL:material_grade, - KEY_SUPTNGSEC_FU: fu, - KEY_SUPTNGSEC_FY: fy, - KEY_SUPTDSEC_FU: fu, - KEY_SUPTDSEC_FY: fy, - KEY_SEC_FU: fu, - KEY_SEC_FY: fy, - KEY_BASE_PLATE_FU: fu, - KEY_BASE_PLATE_FY: fy} - - return d - - def get_fu_fy(self): - material_grade = self[0] - - if material_grade != "Select Material": - m_conn = Material(material_grade) - fu_conn = m_conn.fu - fy_20 = m_conn.fy_20 - fy_20_40 = m_conn.fy_20_40 - fy_40 = m_conn.fy_40 - else: - fu_conn = '' - fy_20 = '' - fy_20_40 = '' - fy_40 = '' - - d = { - KEY_CONNECTOR_FU: fu_conn, - KEY_CONNECTOR_FY_20: fy_20, - KEY_CONNECTOR_FY_20_40: fy_20_40, - KEY_CONNECTOR_FY_40: fy_40} - - return d - - def edit_tabs(self): - """ - - :return: This function is used when the whole tab is conditional i.e., to be changed based on the change in - input dock key. - This returns a list of tuples which contains the key whose change will decide the tab, title of the tab, - function to get tab contents and Type of change (It can be changed(TYPE_CHANGE_TAB) or removed(TYPE_REMOVE_TAB)) - - [Title of tab, Key of input dock, Type of change, function of tab] - - the function of tab has inbuilt arguments of key of input dock passed in this tuple - the fucntion returns name of tab to be displayed. So this fucntion returns list in this format, - [current tab name, key that may cause the change, changed/removed, new tab name] - """ - - edit_list = [] - - t1 = (DISP_TITLE_ANGLE, KEY_SEC_PROFILE, TYPE_REMOVE_TAB, self.get_selected_tab) - edit_list.append(t1) - - t1 = (DISP_TITLE_CHANNEL, KEY_SEC_PROFILE, TYPE_REMOVE_TAB, self.get_selected_tab) - edit_list.append(t1) - - return edit_list - - def get_selected_tab(self): - """ - - :return: This function have key value passed in self. This return name of the tab selected - based on the value of the key passed. - """ - if self in ['Angles', 'Back to Back Angles', 'Star Angles']: - return DISP_TITLE_ANGLE - elif self in [ 'Channels', 'Back to Back Channels']: - return DISP_TITLE_CHANNEL - elif self in['Beams']: - return KEY_DISP_BEAMSEC - else: - return KEY_DISP_COLSEC - - def get_values_for_design_pref(self, key, design_dictionary): - """ - This is used to get default values for design preferences. This is called to get design dictionary, - when design preferences are not opened by the user. (Usually, design preferences is added to input dictionary - when user clicks on 'save' of design preferences) - :param key: list of keys of design preferences to be stored - :param design_dictionary: Input dock design dictionary (since for some keys, input dock values are default) - :return: returns a design preference dictionary which will be appended to input dock dictionary - """ - - if design_dictionary[KEY_MATERIAL] != 'Select Material': - fu = Material(design_dictionary[KEY_MATERIAL], 41).fu - else: - fu = '' - - val = {KEY_DP_BOLT_TYPE: "Pretensioned", - KEY_DP_BOLT_HOLE_TYPE: "Standard", - KEY_DP_BOLT_SLIP_FACTOR: str(0.3), - KEY_DP_WELD_MATERIAL_G_O: fu, - KEY_DP_WELD_FAB: KEY_DP_FAB_SHOP, - KEY_DP_WELD_MATERIAL_G_O: str(fu), - KEY_DP_DETAILING_EDGE_TYPE: "Sheared or hand flame cut", - KEY_DP_DETAILING_GAP: '0', - KEY_DP_DETAILING_CORROSIVE_INFLUENCES: 'No', - KEY_DP_DESIGN_METHOD: "Limit State Design", - KEY_CONNECTOR_MATERIAL: str(design_dictionary[KEY_MATERIAL]) - }[key] - - return val - - def refresh_input_dock(self): - - add_buttons = [] - - t1 = (DISP_TITLE_ANGLE, KEY_SECSIZE, TYPE_COMBOBOX_CUSTOMIZED, KEY_SECSIZE_SELECTED, KEY_SEC_PROFILE, - ['Angles', 'Back to Back Angles', 'Star Angles'], "Angles") - add_buttons.append(t1) - - t1 = (DISP_TITLE_ANGLE, KEY_SECSIZE, TYPE_COMBOBOX_CUSTOMIZED, KEY_SECSIZE_SELECTED, KEY_SEC_PROFILE, - ['Channels', 'Back to Back Channels'], "Channels") - add_buttons.append(t1) - - t1 = (DISP_TITLE_ANGLE, KEY_SECSIZE, TYPE_COMBOBOX_CUSTOMIZED, KEY_SECSIZE_SELECTED, KEY_SEC_PROFILE, - ['Beams'], "Beams") - add_buttons.append(t1) - - t1 = (DISP_TITLE_ANGLE, KEY_SECSIZE, TYPE_COMBOBOX_CUSTOMIZED, KEY_SECSIZE_SELECTED, KEY_SEC_PROFILE, - ['Columns'], "Columns") - add_buttons.append(t1) - - return add_buttons - - def detailing_values(self, input_dictionary): - - values = {KEY_DP_DETAILING_EDGE_TYPE: 'Sheared or hand flame cut', - KEY_DP_DETAILING_GAP:"0", - KEY_DP_DETAILING_CORROSIVE_INFLUENCES: 'No'} - - for key in values.keys(): - if key in input_dictionary.keys(): - values[key] = input_dictionary[key] - - detailing = [] - - t1 = (KEY_DP_DETAILING_EDGE_TYPE, KEY_DISP_DP_DETAILING_EDGE_TYPE, TYPE_COMBOBOX, - ['Sheared or hand flame cut', 'Rolled, machine-flame cut, sawn and planed'], - values[KEY_DP_DETAILING_EDGE_TYPE]) - detailing.append(t1) - - t2 = (KEY_DP_DETAILING_GAP, KEY_DISP_DP_DETAILING_GAP, TYPE_TEXTBOX, None, values[KEY_DP_DETAILING_GAP]) - detailing.append(t2) - - t3 = (KEY_DP_DETAILING_CORROSIVE_INFLUENCES, KEY_DISP_DP_DETAILING_CORROSIVE_INFLUENCES, TYPE_COMBOBOX, - ['No', 'Yes'], values[KEY_DP_DETAILING_CORROSIVE_INFLUENCES]) - detailing.append(t3) - - t4 = ("textBrowser", "", TYPE_TEXT_BROWSER, DETAILING_DESCRIPTION, None) - detailing.append(t4) - - return detailing - - - ######################################## - # Design Preference Functions End - ######################################## - - - # def customized_input(self): - # - # list1 = [] - # t1 = (KEY_GRD, self.grdval_customized) - # list1.append(t1) - # t3 = (KEY_D, self.diam_bolt_customized) - # list1.append(t3) - # t6 = (KEY_PLATETHK, self.plate_thick_customized) - # list1.append(t6) - # # t8 = (KEY_SIZE, self.size_customized) - # # list1.append(t8) - # return list1 - - @staticmethod - def grdval_customized(): - b = VALUES_GRD_CUSTOMIZED - return b - - @staticmethod - def diam_bolt_customized(): - c = connectdb1() - return c - - @staticmethod - def plate_thick_customized(): - d = VALUES_PLATETHK_CUSTOMIZED - return d - - # - # @staticmethod - # def size_customized(): - # d = VALUES_SIZE_CUSTOMIZED - # return d - - # def input_value_changed(self): - # pass - - def set_input_values(self, design_dictionary): - pass - # self.mainmodule = "Tension" - # self.connectivity = design_dictionary[KEY_CONN] - - # if self.connectivity in VALUES_CONN_1: - # self.supporting_section = Column(designation=design_dictionary[KEY_SUPTNGSEC], material_grade=design_dictionary[KEY_MATERIAL]) - # else: - # self.supporting_section = Beam(designation=design_dictionary[KEY_SUPTNGSEC], material_grade=design_dictionary[KEY_MATERIAL]) - - - def new_material(self): - - selected_material = self[0] - if selected_material in ["Custom","Custom Section"]: - return True - else: - return False - - ###################################### - # Function for individual component calls in 3D view - ###################################### - def get_3d_components(self): - components = [] - - t1 = ('Model', self.call_3DModel) - components.append(t1) - - t1 = ('Member', self.call_3DMember) - components.append(t1) - - t2 = ('Plate', self.call_3DPlate) - components.append(t2) - - t3 = ('Endplate', self.call_3DEndplate) - components.append(t3) - - return components - - def call_3DPlate(self, ui, bgcolor): - from PyQt5.QtWidgets import QCheckBox - from PyQt5.QtCore import Qt - for chkbox in ui.frame.children(): - if chkbox.objectName() == 'Plate': - continue - if isinstance(chkbox, QCheckBox): - chkbox.setChecked(Qt.Unchecked) - ui.commLogicObj.display_3DModel("Plate", bgcolor) - - def call_3DMember(self, ui, bgcolor): - from PyQt5.QtWidgets import QCheckBox - from PyQt5.QtCore import Qt - for chkbox in ui.frame.children(): - if chkbox.objectName() == 'Member': - continue - if isinstance(chkbox, QCheckBox): - chkbox.setChecked(Qt.Unchecked) - ui.commLogicObj.display_3DModel("Member", bgcolor) - - - def call_3DEndplate(self, ui, bgcolor): - from PyQt5.QtWidgets import QCheckBox - from PyQt5.QtCore import Qt - for chkbox in ui.frame.children(): - if chkbox.objectName() == 'Endplate': - continue - if isinstance(chkbox, QCheckBox): - chkbox.setChecked(Qt.Unchecked) - ui.commLogicObj.display_3DModel("Endplate", bgcolor) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..121708a02 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,40 @@ +version: '3' + +services: + backend: + build: + context: . + dockerfile: Dockerfile + ports: + - "8000:8000" + volumes: + - .:/app + depends_on: + - db + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + ports: + - "5173:5173" + volumes: + - ./frontend:/app + environment: + - CHOKIDAR_USEPOLLING=true + command: npm run dev + + + db: + image: postgres:14 + environment: + POSTGRES_USER: myuser + POSTGRES_PASSWORD: mypassword + POSTGRES_DB: mydb + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: diff --git a/documentation/REPORT_IMAGE_GENERATION.md b/documentation/REPORT_IMAGE_GENERATION.md new file mode 100644 index 000000000..9ca2d9b82 --- /dev/null +++ b/documentation/REPORT_IMAGE_GENERATION.md @@ -0,0 +1,267 @@ + +## Problem Statement + +When generating design reports via the web API, CAD images (3D, front, top, side views) were either: +1. **Missing** - Causing LaTeX compilation errors (`File 'None' not found`) +2. **Colorless/White** - Images were generated but appeared as blank white canvases without any geometry or colors +3. **Single-color** - Images showed geometry but without the distinct colors for different parts (beams, columns, plates, bolts, welds) that users see in the GUI + +### Root Causes + +1. **Missing Image Generation**: The CLI approach (`save_design()`) expects images to already exist, but the web API wasn't generating them before calling `save_design()`. + +2. **Incorrect Rendering Method**: Initial attempts used VTK or Open3D rendering, which: + - Don't support the colored rendering used by the GUI + - Produce single-color or grayscale images + - Don't replicate the exact visual appearance of the GUI + +3. **BREP File Approach**: Loading pre-generated BREP files directly loses color information, as colors are applied during CAD generation, not stored in BREP files. + +## Solution + +The solution replicates the exact GUI rendering approach using: +- **Qt/PySide6** with OpenCASCADE display (same as GUI) +- **Module-based CAD generation** - Uses the module's own `call_3DModel()` and `display_3DModel()` methods +- **Headless rendering** - Uses Qt's offscreen platform plugin with Xvfb + +### Architecture + +``` +Web API Request + ↓ +generate_cad_images_for_report() + ↓ +generate_with_opencascade_display() [PRIMARY - WITH COLORS] + ├─ Setup headless Qt (QT_QPA_PLATFORM=offscreen) + ├─ Create offscreen display (pyside6 backend) + ├─ Create CommonDesignLogic instance + ├─ Call module.call_3DModel() → Creates CAD with colored parts + ├─ Call display_3DModel() → Displays with colors + ├─ Set white background for reports + └─ Export 4 views: 3d.png, front.png, top.png, side.png + ↓ +[If Qt fails] +generate_with_vtk() [FALLBACK - NO COLORS] + └─ Single-color rendering +``` + +## Setup Requirements + +### System Packages + +#### 1. Xvfb (X Virtual Framebuffer) +Required for headless Qt rendering. + +```bash +sudo apt-get update +sudo apt-get install xvfb +``` + +**Why needed**: Qt's offscreen platform plugin requires a display server. Xvfb provides a virtual display for headless environments. + +#### 2. libxcb-cursor0 +Required for Qt's xcb platform plugin (even when using offscreen). + +```bash +sudo apt-get install libxcb-cursor0 +``` + +**Why needed**: Qt 6.5+ requires this library for the xcb platform plugin, even when using offscreen rendering. + +### Python Packages + +All required packages should already be in `requirements.txt`: + +- **PySide6** - Qt Python bindings (already installed) +- **pythonocc-core** - OpenCASCADE Python bindings (already installed) +- **vtk** - VTK library (fallback rendering, already installed) + +### Verification + +Check if Xvfb is installed: +```bash +which Xvfb +``` + +Check if libxcb-cursor0 is installed: +```bash +dpkg -l | grep libxcb-cursor +``` + +## How It Works + +### 1. Module CAD Generation + +Instead of loading pre-generated BREP files, the solution uses the module's own CAD generation logic: + +```python +# Create CommonDesignLogic (same as GUI) +comm_logic = CommonDesignLogic( + display=off_display, + cad_widget=mock_widget, + folder=folder, + connection=connection, + mainmodule=mainmodule +) + +# Call module's CAD generation (creates colored parts) +comm_logic.call_3DModel(design_status, module) + +# Display with colors (like GUI) +comm_logic.display_3DModel("Model", "gradient_light") +``` + +This ensures: +- **Individual parts are created** with their respective colors (beams, plates, bolts, welds) +- **Colors match the GUI** exactly +- **No BREP conversion needed** - colors are applied during generation + +### 2. Headless Qt Setup + +```python +# Set Qt to use offscreen platform (must be before Qt imports) +os.environ['QT_QPA_PLATFORM'] = 'offscreen' + +# Create offscreen display with pyside6 backend +off_display, _, _, _ = init_display_off_screen(backend_str='pyside6') +``` + +### 3. Image Export + +After displaying the model with colors: + +```python +# Set white background for reports +off_display.set_bg_gradient_color([255, 255, 255], [126, 126, 126]) + +# Export 4 views +off_display.ExportToImage('3d.png') # 3D view +off_display.View_Front() +off_display.FitAll() +off_display.Repaint() +off_display.ExportToImage('front.png') # Front view +# ... (top.png, side.png) +``` + +## File Structure + +``` +Osdag-web/ +├── backend/ +│ └── apps/ +│ └── core/ +│ └── utils/ +│ └── report_image_generator.py # Main implementation +├── osdag_core/ +│ ├── cad/ +│ │ └── common_logic.py # CommonDesignLogic (modified for headless) +│ └── texlive/ +│ └── Design_wrapper.py # Display initialization +└── backend/ + └── file_storage/ + └── design_report/ + └── ResourceFiles/ + └── images/ # Generated images stored here + ├── 3d.png + ├── front.png + ├── top.png + └── side.png +``` + +## Key Code Changes + +### 1. `report_image_generator.py` + +- **`generate_with_opencascade_display()`**: Primary rendering function using Qt/OpenCASCADE +- **Module-based CAD generation**: Uses `CommonDesignLogic.call_3DModel()` instead of BREP loading +- **Fallback chain**: Qt → VTK → Open3D → broken.png + +### 2. `common_logic.py` + +- **Optional cleanup coordinator**: Made `osdag_gui.OS_safety_protocols` import optional for headless mode +- **Headless-compatible**: Works without GUI dependencies + +### 3. `Design_wrapper.py` + +- **Removed verbose print**: Cleaned up debug output + +## Troubleshooting + +### Issue: "Xvfb not found" +**Solution**: Install Xvfb (see Setup Requirements) + +### Issue: "Could not load the Qt platform plugin 'xcb'" +**Solution**: Install `libxcb-cursor0` (see Setup Requirements) + +### Issue: "'Viewer3d' object has no attribute 'Update'" +**Solution**: Use `Repaint()` instead of `Update()` for OpenCASCADE display objects. + +### Issue: Images are still white/colorless +**Check**: +1. Is Qt rendering being used? Check logs for `[REPORT_IMG] ATTEMPTING Qt/OpenCASCADE RENDERING` +2. Is `call_3DModel()` succeeding? Check logs for `connectivityObj exists` +3. Are colors being applied? Check that `display_3DModel()` is called with "Model" component + +### Issue: "Module CAD generation failed" +**Possible causes**: +1. Module object is None or invalid +2. Module doesn't have required attributes (`design_status`, `module`, `mainmodule`) +3. CAD generation function doesn't exist for this module type + +**Solution**: Ensure module is properly initialized before calling `generate_cad_images_for_report()` + +## Testing + +### Manual Test + +1. Generate a report via web API +2. Check generated images in `backend/file_storage/design_report/ResourceFiles/images/` +3. Verify: + - Images exist (3d.png, front.png, top.png, side.png) + - Images show geometry (not blank) + - Images have colors (beams, plates, bolts in different colors) + - Colors match GUI appearance + +### Automated Test + +```python +from backend.apps.core.utils.report_image_generator import generate_cad_images_for_report + +# Create module instance +module = create_from_input(input_values) + +# Generate images +result = generate_cad_images_for_report(module, output_dir) + +# Verify +assert result['success'] == True +assert '3d' in result['images'] +assert os.path.exists(result['images']['3d']) +``` + +## Performance Considerations + +- **Qt rendering**: ~2-5 seconds per report (with colors) +- **VTK fallback**: ~1-2 seconds per report (no colors) +- **Image generation**: Happens synchronously before `save_design()` call + +## Future Improvements + +1. **Caching**: Cache generated images if design parameters haven't changed +2. **Async generation**: Generate images asynchronously to improve API response time +3. **Image optimization**: Compress images or use different formats for smaller file sizes +4. **Error recovery**: Better fallback handling and error messages + +## Related Files + +- `backend/apps/core/utils/report_image_generator.py` - Main implementation +- `backend/apps/core/api/design/design_report_csv_view.py` - API endpoint that calls image generation +- `osdag_core/design_report/reportGenerator_latex.py` - LaTeX report generator (uses images) +- `osdag_core/cad/common_logic.py` - CAD generation logic (used by both GUI and web) + +## References + +- OpenCASCADE Documentation: https://dev.opencascade.org/ +- Qt Offscreen Rendering: https://doc.qt.io/qt-6/qoffscreensurface.html +- PySide6 Documentation: https://doc.qt.io/qtforpython/ + diff --git a/documentation/SAMPLE_INPUTS_SIMPLE_CONNECTIONS.md b/documentation/SAMPLE_INPUTS_SIMPLE_CONNECTIONS.md new file mode 100644 index 000000000..e5e643dc5 --- /dev/null +++ b/documentation/SAMPLE_INPUTS_SIMPLE_CONNECTIONS.md @@ -0,0 +1,162 @@ +# Sample Inputs for Simple Connection Modules + +## 1. Butt Joint Bolted + +```json +{ + "Module": "ButtJointBolted", + "Material": "E 250 (Fe 410 W)A", + "Load.Axial": "100", + "Plate1Thickness": "12", + "Plate2Thickness": "12", + "PlateWidth": "200", + "ButtJoint.CoverPlate": "Single-Cover", + "Bolt.Diameter": "16", + "Bolt.Grade": "4.6", + "Bolt.Type": "Bearing Bolt", + "Bolt.Bolt_Hole_Type": "Standard", + "Bolt.Slip_Factor": "0.33", + "Design.For": "Tension", + "Detailing.Edge_type": "Sheared or hand flame cut", + "Detailing.Packing_Plate": "No" +} +``` + +## 2. Butt Joint Welded + +```json +{ + "Module": "ButtJointWelded", + "Material": "E 250 (Fe 410 W)A", + "Load.Axial": "100", + "Plate1Thickness": "10", + "Plate2Thickness": "14", + "PlateWidth": "200", + "ButtJoint.CoverPlate": "Single-Cover", + "Weld.Size": "6", + "Weld.Type": "Shop weld", + "Weld.Material_Grade_OverWrite": "410", + "Weld.Fab": "Shop Weld", + "Design.For": "Tension", + "Detailing.Edge_type": "Sheared or hand flame cut", + "Detailing.Packing_Plate": "No" +} +``` + +**Note:** Weld size must be between minimum (5 mm) and maximum (typically 8.5-10.5 mm depending on plate thickness). Use 5, 6, 7, or 8 mm for best results. + +## 3. Lap Joint Bolted + +```json +{ + "Module": "LapJointBolted", + "Material": "E 250 (Fe 410 W)A", + "Load.Axial": "100", + "Plate1Thickness": "12", + "Plate2Thickness": "12", + "PlateWidth": "200", + "ButtJoint.CoverPlate": "Single-Cover", + "Bolt.Diameter": "16", + "Bolt.Grade": "4.6", + "Bolt.Type": "Bearing Bolt", + "Bolt.Bolt_Hole_Type": "Standard", + "Bolt.Slip_Factor": "0.33", + "Design.For": "Tension", + "Detailing.Edge_type": "Sheared or hand flame cut", + "Detailing.Packing_Plate": "No" +} +``` + +## 4. Lap Joint Welded + +```json +{ + "Module": "LapJointWelded", + "Material": "E 250 (Fe 410 W)A", + "Load.Axial": "100", + "Plate1Thickness": "10", + "Plate2Thickness": "12", + "PlateWidth": "200", + "ButtJoint.CoverPlate": "Single-Cover", + "Weld.Size": "6", + "Weld.Type": "Shop weld", + "Weld.Material_Grade_OverWrite": "410", + "Weld.Fab": "Shop Weld", + "Design.For": "Tension", + "Detailing.Edge_type": "Sheared or hand flame cut", + "Detailing.Packing_Plate": "No" +} +``` + +**Note:** For welded connections, ensure: +- Plate thicknesses are reasonable (8-20 mm recommended) +- Weld size is between 5-8 mm (check terminal logs for exact min/max based on plate thickness) +- For plates with thickness 10-12 mm, max weld size is typically 8.5 mm +- For plates with thickness 12-14 mm, max weld size is typically 10.5 mm + +## Common Input Ranges + +### Material Options +- `"E 250 (Fe 410 W)A"` +- `"E 250 (Fe 410 W)B"` +- `"E 250 (Fe 410 W)C"` +- `"E 350 (Fe 440 W)A"` +- `"E 350 (Fe 440 W)B"` +- `"E 350 (Fe 440 W)C"` + +### Bolt Diameter Options (mm) +- `"8"`, `"10"`, `"12"`, `"14"`, `"16"`, `"18"`, `"20"`, `"22"`, `"24"`, `"27"`, `"30"` + +### Bolt Grade Options +- `"3.6"`, `"4.6"`, `"4.8"`, `"5.6"`, `"5.8"`, `"6.8"`, `"8.8"`, `"9.8"`, `"10.9"`, `"12.9"` + +### Bolt Type Options +- `"Bearing Bolt"` +- `"HSFG Bolt"` + +### Weld Size Options (mm) +- `"4"`, `"5"`, `"6"`, `"8"`, `"10"`, `"12"` (must be within min/max based on plate thickness) + +### Weld Type Options +- `"Shop weld"` +- `"Field weld"` + +### Cover Plate Options +- `"Single-Cover"` +- `"Double-Cover"` + +### Design For Options +- `"Tension"` +- `"Compression"` + +### Edge Type Options +- `"Sheared or hand flame cut"` +- `"Machine flame cut, sawn and planed"` +- `"Rolled edge"` + +### Packing Plate Options +- `"No"` +- `"Yes"` + +## Testing Tips + +1. **For Welded Connections:** + - Check terminal logs for exact min/max weld size + - If you see "Selected weld size is not suitable", try a size between the min and max shown in logs + - Example: If min=5 mm and max=8.5 mm, use 5, 6, 7, or 8 mm + +2. **For Bolted Connections:** + - Ensure bolt diameter is appropriate for the load + - Smaller loads (10-100 N) work well with 8-12 mm bolts + - Larger loads (100-1000 N) may need 16-24 mm bolts + +3. **Load Values:** + - Start with smaller loads (10-100 N) for testing + - Increase gradually to test different scenarios + - Use realistic values based on your application + +4. **Plate Dimensions:** + - Width: 150-300 mm is typical + - Thickness: 8-20 mm is common + - Ensure plate1 and plate2 thicknesses are reasonable + diff --git a/documentation/VALIDATION_SYSTEM.md b/documentation/VALIDATION_SYSTEM.md new file mode 100644 index 000000000..55c31c6f6 --- /dev/null +++ b/documentation/VALIDATION_SYSTEM.md @@ -0,0 +1,636 @@ +# Validation System Documentation + +## Overview + +The Osdag-Web validation system provides a DRY (Don't Repeat Yourself) approach to input validation across modules. It consists of: + +1. **Backend Validation**: Shared validators for Python adapters +2. **Frontend Validation**: JavaScript validation utilities for React components +3. **Error Handling**: Structured error responses with appropriate HTTP status codes + +This system was initially implemented for Simple Connection modules and can be extended to other modules. + +--- + +## Table of Contents + +1. [Backend Validation](#backend-validation) +2. [Frontend Validation](#frontend-validation) +3. [Error Handling](#error-handling) +4. [Using Validators in Other Modules](#using-validators-in-other-modules) +5. [Examples](#examples) +6. [Best Practices](#best-practices) + +--- + +## Backend Validation + +### Location + +- **Shared Validator**: `backend/apps/modules/simple_connection/shared_validation.py` +- **Error Classes**: `backend/apps/core/utils/errors.py` + +### Components + +#### 1. SimpleConnectionValidator Class + +A configurable validator class that handles: +- Required key validation +- Type validation (numeric, list, string) +- Range validation (min/max values) +- Module-specific field validation (bolts, welds) + +#### 2. Factory Functions + +Pre-configured validators for common use cases: +- `create_bolted_validator()`: For bolted connections +- `create_welded_validator()`: For welded connections + +#### 3. Exception Classes + +- `MissingKeyError`: Raised when required keys are missing +- `InvalidInputTypeError`: Raised when input type is incorrect +- `RangeValidationError`: Raised when values are outside allowed range +- `ValidationError`: Raised when multiple validation errors occur + +### Usage in Adapters + +#### Basic Example + +```python +from ...shared_validation import create_bolted_validator + +def get_required_keys() -> List[str]: + return [ + "Module", + "Material", + "Load.Axial", + "PlateWidth", + # ... other required keys + ] + +# Create validator instance (module-level) +_validator = create_bolted_validator(get_required_keys(), "YourModuleName") + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate input values using shared validator""" + _validator.validate(input_values) +``` + +#### Custom Validator Example + +If you need custom validation rules: + +```python +from ...shared_validation import SimpleConnectionValidator + +# Create custom validator +_validator = (SimpleConnectionValidator(get_required_keys(), "YourModuleName") + .add_range_validation("PlateWidth", min_value=0.0, allow_zero=False) + .add_range_validation("Load.Axial", min_value=0.0, allow_zero=False) + .add_range_validation("CustomField", min_value=10.0, max_value=100.0, allow_zero=False)) + +def validate_input(input_values: Dict[str, Any]) -> None: + _validator.validate(input_values) +``` + +### Range Validation Options + +The `add_range_validation()` method accepts: + +- `key` (str): The field name to validate +- `min_value` (float): Minimum allowed value (default: 0.0) +- `max_value` (Optional[float]): Maximum allowed value (default: None) +- `allow_zero` (bool): Whether zero is allowed (default: False) + +**Examples:** + +```python +# Positive values only (> 0) +.add_range_validation("PlateWidth", min_value=0.0, allow_zero=False) + +# Zero or positive (>= 0) +.add_range_validation("OptionalField", min_value=0.0, allow_zero=True) + +# Bounded range +.add_range_validation("SlipFactor", min_value=0.0, max_value=1.0, allow_zero=False) + +# Minimum threshold +.add_range_validation("Thickness", min_value=5.0, allow_zero=False) +``` + +### What Gets Validated + +The `SimpleConnectionValidator` automatically validates: + +1. **Required Keys**: Checks all keys in `required_keys` list +2. **Bolt Fields** (if present): + - `Bolt.Diameter`: Must be `List[str]` convertible to int + - `Bolt.Grade`: Must be `List[str]` convertible to float +3. **Weld Fields** (if present): + - `Weld.Size`: Must be `List[str]` convertible to float (normalizes single values to list) +4. **Numeric Fields**: Validates and normalizes: + - `Plate1Thickness`, `Plate2Thickness` + - `PlateWidth` + - `Load.Axial` + - `Bolt.Slip_Factor` +5. **Range Validations**: All fields added via `add_range_validation()` + +### Value Normalization + +The validator automatically normalizes values: + +- **Lists/Tuples**: Extracts first element if single-item list +- **Numeric Types**: Converts `int`/`float` to `str` for consistency +- **Weld Size**: Converts single values to list format + +--- + +## Frontend Validation + +### Location + +- **Shared Validator**: `frontend/src/modules/SimpleConnection/shared/validation.js` + +### Function + +```javascript +validateSimpleConnectionInputs(inputs, options) +``` + +**Parameters:** +- `inputs` (Object): Input values object +- `options` (Object): Validation options + - `moduleType` (string): `'bolted'` or `'welded'` + +**Returns:** +```javascript +{ + isValid: boolean, + errors: Array<{field: string, message: string}>, + message: string // Combined error messages +} +``` + +### Usage in Config Files + +```javascript +import { validateSimpleConnectionInputs } from "../../shared/validation"; + +export const yourModuleConfig = { + // ... other config + validateInputs: (inputs) => { + return validateSimpleConnectionInputs(inputs, { + moduleType: 'bolted' // or 'welded' + }); + }, + // ... rest of config +}; +``` + +### What Gets Validated + +**Common Validations (all modules):** +- `material`: Required, not empty +- `plate_width`: Required, must be > 0 +- `axial_force`: Required, must be > 0 +- `plate1_thickness`: Required, must be > 0 +- `plate2_thickness`: Required, must be > 0 + +**Bolted Connections (`moduleType: 'bolted'`):** +- `bolt_diameter`: At least one must be selected +- `bolt_grade`: At least one must be selected +- `bolt_slip_factor`: Must be between 0 and 1.0 (if provided) + +**Welded Connections (`moduleType: 'welded'`):** +- `weld_size`: Required, must be > 0 + +### Error Response Format + +```javascript +{ + isValid: false, + errors: [ + { field: 'plate_width', message: 'Plate width must be greater than zero' }, + { field: 'axial_force', message: 'Axial force must be greater than zero' } + ], + message: 'Plate width must be greater than zero; Axial force must be greater than zero' +} +``` + +--- + +## Error Handling + +### Backend Error Response Format + +All validation errors are returned in a consistent format: + +```json +{ + "success": false, + "error": { + "type": "validation_error" | "range_validation_error" | "value_error" | "server_error", + "message": "Human-readable error message", + "details": [ + { + "field": "PlateWidth", + "message": "PlateWidth value -5 violates constraint: minimum 0.0 (exclusive)" + } + ] + } +} +``` + +### HTTP Status Codes + +- **400 Bad Request**: Validation errors (MissingKeyError, InvalidInputTypeError, RangeValidationError) +- **422 Unprocessable Entity**: Value errors (ValueError) +- **500 Internal Server Error**: Unexpected server errors + +### Using Error Handling in ViewSets + +```python +from apps.core.utils.errors import format_error_response, get_error_status_code +import logging + +logger = logging.getLogger(__name__) + +class YourViewSet(viewsets.ViewSet): + @action(detail=False, methods=['post']) + def design(self, request): + try: + # Your design logic + result = ServiceClass.calculate(inputs=inputs) + return Response(result, status=200) + except Exception as exc: + logger.error(f"Error in design: {exc}", exc_info=True) + error_response = format_error_response(exc) + status_code = get_error_status_code(exc) + return Response(error_response, status=status_code) +``` + +--- + +## Using Validators in Other Modules + +### Step 1: Create Shared Validator (if needed) + +If your module has unique validation requirements, create a shared validator: + +**File**: `backend/apps/modules/your_module/shared_validation.py` + +```python +from typing import Dict, Any, List +from apps.core.utils import MissingKeyError, InvalidInputTypeError +from apps.core.utils.errors import RangeValidationError +from apps.modules.simple_connection.shared_validation import SimpleConnectionValidator + +def create_your_module_validator(required_keys: List[str], module_name: str): + """Create validator for your module""" + return (SimpleConnectionValidator(required_keys, module_name) + .add_range_validation("YourField1", min_value=0.0, allow_zero=False) + .add_range_validation("YourField2", min_value=10.0, max_value=100.0, allow_zero=False)) +``` + +### Step 2: Update Adapter + +**File**: `backend/apps/modules/your_module/submodules/your_submodule/adapter.py` + +```python +from ...shared_validation import create_your_module_validator + +def get_required_keys() -> List[str]: + return ["Module", "Material", "YourField1", "YourField2"] + +# Create validator instance +_validator = create_your_module_validator(get_required_keys(), "YourModuleName") + +def validate_input(input_values: Dict[str, Any]) -> None: + """Validate input values using shared validator""" + _validator.validate(input_values) +``` + +### Step 3: Update ViewSet Error Handling + +**File**: `backend/apps/modules/your_module/views.py` + +```python +from apps.core.utils.errors import format_error_response, get_error_status_code +import logging + +logger = logging.getLogger(__name__) + +class YourViewSet(viewsets.ViewSet): + @action(detail=False, methods=['post']) + def design(self, request): + try: + # Your logic + result = ServiceClass.calculate(inputs=inputs) + return Response(result, status=200) + except Exception as exc: + logger.error(f"Error: {exc}", exc_info=True) + error_response = format_error_response(exc) + status_code = get_error_status_code(exc) + return Response(error_response, status=status_code) +``` + +### Step 4: Create Frontend Validator (if needed) + +**File**: `frontend/src/modules/YourModule/shared/validation.js` + +```javascript +/** + * Validates your module inputs + */ +export function validateYourModuleInputs(inputs, options = {}) { + const errors = []; + + // Required fields + if (!inputs.your_field || inputs.your_field.trim() === '') { + errors.push({ field: 'your_field', message: 'Your field is required' }); + } else { + const value = parseFloat(inputs.your_field); + if (isNaN(value) || value <= 0) { + errors.push({ field: 'your_field', message: 'Your field must be greater than zero' }); + } + } + + // Add more validations... + + const message = errors.length > 0 + ? errors.map(e => e.message).join('; ') + : ''; + + return { + isValid: errors.length === 0, + errors: errors, + message: message + }; +} +``` + +### Step 5: Update Frontend Config + +**File**: `frontend/src/modules/YourModule/YourSubmodule/config/yourConfig.js` + +```javascript +import { validateYourModuleInputs } from "../../shared/validation"; + +export const yourConfig = { + // ... other config + validateInputs: (inputs) => { + return validateYourModuleInputs(inputs, { + // your options + }); + }, + // ... rest of config +}; +``` + +--- + +## Examples + +### Example 1: Simple Bolted Connection + +**Backend Adapter:** + +```python +from ...shared_validation import create_bolted_validator + +def get_required_keys() -> List[str]: + return [ + "Module", "Material", "Load.Axial", + "Plate1Thickness", "Plate2Thickness", "PlateWidth", + "Bolt.Diameter", "Bolt.Grade", "Bolt.Slip_Factor" + ] + +_validator = create_bolted_validator(get_required_keys(), "SimpleBolted") + +def validate_input(input_values: Dict[str, Any]) -> None: + _validator.validate(input_values) +``` + +**Frontend Config:** + +```javascript +import { validateSimpleConnectionInputs } from "../../shared/validation"; + +export const simpleBoltedConfig = { + validateInputs: (inputs) => { + return validateSimpleConnectionInputs(inputs, { + moduleType: 'bolted' + }); + }, +}; +``` + +### Example 2: Custom Validator with Additional Fields + +**Backend Adapter:** + +```python +from ...shared_validation import SimpleConnectionValidator + +def get_required_keys() -> List[str]: + return ["Module", "Material", "Length", "Width", "Height"] + +_validator = (SimpleConnectionValidator(get_required_keys(), "CustomModule") + .add_range_validation("Length", min_value=100.0, allow_zero=False) + .add_range_validation("Width", min_value=50.0, max_value=200.0, allow_zero=False) + .add_range_validation("Height", min_value=0.0, allow_zero=True)) # Allow zero + +def validate_input(input_values: Dict[str, Any]) -> None: + _validator.validate(input_values) +``` + +### Example 3: Handling Validation Errors + +**ViewSet:** + +```python +from apps.core.utils.errors import format_error_response, get_error_status_code +import logging + +logger = logging.getLogger(__name__) + +@action(detail=False, methods=['post']) +def design(self, request): + try: + validate_input(request.data.get('inputs', {})) + result = ServiceClass.calculate(inputs=inputs) + return Response(result, status=200) + except MissingKeyError as e: + # Missing required field + error_response = format_error_response(e) + return Response(error_response, status=400) + except RangeValidationError as e: + # Value out of range + error_response = format_error_response(e) + return Response(error_response, status=400) + except Exception as exc: + # Unexpected error + logger.error(f"Unexpected error: {exc}", exc_info=True) + error_response = format_error_response(exc) + status_code = get_error_status_code(exc) + return Response(error_response, status=status_code) +``` + +--- + +## Best Practices + +### 1. Use Factory Functions When Possible + +Prefer factory functions (`create_bolted_validator`, `create_welded_validator`) over creating validators from scratch when they match your needs. + +### 2. Create Module-Specific Validators + +If multiple submodules share validation logic, create a shared validator in the module's root directory: + +``` +your_module/ +├── shared_validation.py # Shared validator +├── submodules/ +│ ├── submodule1/ +│ │ └── adapter.py # Uses shared validator +│ └── submodule2/ +│ └── adapter.py # Uses shared validator +``` + +### 3. Keep Validation Logic DRY + +- Don't duplicate validation logic across adapters +- Use shared validators for common patterns +- Extend `SimpleConnectionValidator` for custom needs + +### 4. Provide Clear Error Messages + +Ensure error messages are: +- User-friendly +- Specific to the field +- Include the actual value when helpful + +### 5. Validate Early + +Validate inputs as early as possible: +- Frontend: Before API submission +- Backend: In the adapter's `validate_input()` function + +### 6. Log Server Errors + +Always log unexpected errors with full traceback: + +```python +logger.error(f"Error in module: {exc}", exc_info=True) +``` + +### 7. Use Appropriate HTTP Status Codes + +- `400`: Client errors (validation, missing fields) +- `422`: Unprocessable entity (value errors) +- `500`: Server errors (unexpected exceptions) + +### 8. Test Validation + +Test your validators with: +- Valid inputs (should pass) +- Missing required fields (should fail) +- Invalid types (should fail) +- Out-of-range values (should fail) +- Zero/negative values (should fail if not allowed) + +--- + +## Extending the System + +### Adding New Validation Types + +To add new validation types (e.g., email, URL, regex patterns): + +1. **Extend SimpleConnectionValidator:** + +```python +class SimpleConnectionValidator: + def add_email_validation(self, key: str): + """Add email validation""" + # Implementation + return self + + def add_regex_validation(self, key: str, pattern: str): + """Add regex validation""" + # Implementation + return self +``` + +2. **Update Frontend Validator:** + +```javascript +export function validateYourModuleInputs(inputs, options = {}) { + const errors = []; + + // Email validation + if (inputs.email && !isValidEmail(inputs.email)) { + errors.push({ field: 'email', message: 'Invalid email format' }); + } + + // Regex validation + if (inputs.code && !/^[A-Z0-9]+$/.test(inputs.code)) { + errors.push({ field: 'code', message: 'Code must be uppercase alphanumeric' }); + } + + return { isValid: errors.length === 0, errors, message: errors.map(e => e.message).join('; ') }; +} +``` + +### Adding Module-Specific Validations + +For module-specific validations, extend the validator in your module: + +```python +class YourModuleValidator(SimpleConnectionValidator): + def _validate_custom_field(self, iv: Dict[str, Any]) -> None: + """Validate custom field specific to your module""" + if "CustomField" in iv: + # Your validation logic + pass +``` + +--- + +## Troubleshooting + +### Common Issues + +1. **Validation passes but design fails:** + - Check if all required fields are in `required_keys` + - Verify range validations cover all numeric fields + +2. **Frontend validation not working:** + - Ensure `validateInputs` is not commented out + - Check that the function returns `{ isValid, errors, message }` + +3. **Error response format incorrect:** + - Use `format_error_response()` in ViewSet + - Check that exceptions inherit from `OsdagApiException` + +4. **Value normalization issues:** + - The validator normalizes lists/tuples to first element + - Numeric values are converted to strings + - Check if your code expects specific types + +--- + +## Summary + +The validation system provides: + +✅ **DRY validation logic** across modules +✅ **Consistent error responses** with appropriate HTTP status codes +✅ **Frontend and backend validation** for better UX +✅ **Extensible architecture** for custom validation needs +✅ **Clear error messages** for debugging and user feedback + +For questions or issues, refer to the Simple Connection modules as reference implementations. + diff --git a/documentation/backend/ENDPOINT_FEATURES.md b/documentation/backend/ENDPOINT_FEATURES.md new file mode 100644 index 000000000..dd200605e --- /dev/null +++ b/documentation/backend/ENDPOINT_FEATURES.md @@ -0,0 +1,242 @@ +# Design Endpoint Features + +## Overview +The new modular design endpoints (`/api/modules/shear-connection/{submodule}/design/`) support: + +1. **Guest Mode** - Unauthenticated users can run calculations +2. **Project Saving** - Authenticated users can save results to projects +3. **Backward Compatibility** - Works with existing frontend code + +## API Endpoint + +### URL Pattern +``` +POST /api/modules/shear-connection/{submodule_slug}/design/ +``` + +**Examples:** +- `POST /api/modules/shear-connection/fin-plate/design/` +- `POST /api/modules/shear-connection/cleat-angle/design/` +- `POST /api/modules/shear-connection/end-plate/design/` +- `POST /api/modules/shear-connection/seated-angle/design/` + +### Request Body + +**Format 1: With project_id (for authenticated users)** +```json +{ + "inputs": { + "Bolt.Diameter": "16", + "Bolt.Grade": "8.8", + "Load.Shear": "180", + // ... other design inputs + }, + "project_id": 123 +} +``` + +**Format 2: Without project_id (for guests or one-off calculations)** +```json +{ + "Bolt.Diameter": "16", + "Bolt.Grade": "8.8", + "Load.Shear": "180", + // ... other design inputs +} +``` + +### Response + +**Success Response (200 OK):** +```json +{ + "data": { + // Design calculation results + }, + "logs": [ + // Calculation logs + ], + "success": true, + "project_saved": true, // Only if project_id provided and user authenticated + "project_id": 123 // Only if project_id provided and user authenticated +} +``` + +**Error Response (400 Bad Request):** +```json +{ + "error": "Error message here", + "success": false +} +``` + +**Not Found Response (404):** +```json +{ + "error": "Sub-module fin-plate not found" +} +``` + +## Guest Mode + +### How It Works +- **Permission**: `AllowAny` - No authentication required +- **Detection**: Checks JWT token for `is_guest: true` flag +- **Limitations**: + - Cannot save to projects + - `project_id` is ignored if provided + +### Example Request (Guest) +```bash +# No Authorization header needed +curl -X POST http://localhost:8000/api/modules/shear-connection/fin-plate/design/ \ + -H "Content-Type: application/json" \ + -d '{ + "inputs": { + "Bolt.Diameter": "16", + "Load.Shear": "180" + } + }' +``` + +### Example Response (Guest) +```json +{ + "data": { /* results */ }, + "logs": [ /* logs */ ], + "success": true, + "project_saved": false, + "project_error": "Guest users cannot save to projects" +} +``` + +## Project Saving (Authenticated Users) + +### How It Works +1. User must be authenticated (not a guest) +2. `project_id` must be provided in request body +3. Project must exist and belong to the user +4. Project is updated with latest `inputs_json` + +### Example Request (Authenticated) +```bash +curl -X POST http://localhost:8000/api/modules/shear-connection/fin-plate/design/ \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " \ + -d '{ + "inputs": { + "Bolt.Diameter": "16", + "Load.Shear": "180" + }, + "project_id": 123 + }' +``` + +### Example Response (Authenticated with Project) +```json +{ + "data": { /* results */ }, + "logs": [ /* logs */ ], + "success": true, + "project_saved": true, + "project_id": 123 +} +``` + +### Error Cases + +**Project Not Found:** +```json +{ + "data": { /* results */ }, + "logs": [ /* logs */ ], + "success": true, + "project_saved": false, + "project_error": "Project not found or access denied" +} +``` + +**Guest User Attempting to Save:** +```json +{ + "data": { /* results */ }, + "logs": [ /* logs */ ], + "success": true, + "project_saved": false, + "project_error": "Guest users cannot save to projects" +} +``` + +## Implementation Details + +### ViewSet (`backend/apps/modules/shear_connection/views.py`) +- Uses `AllowAny` permission class +- Helper functions: `is_guest_user()`, `get_user_email()` +- Extracts `inputs` and `project_id` from request +- Calls service with context +- Handles project saving after calculation + +### Service Layer (`backend/apps/modules/shear_connection/submodules/*/service.py`) +- Accepts optional `request`, `project_id`, `user_email` parameters +- Focuses on calculation logic only +- Returns calculation results + +### Project Model Update +- Updates `inputs_json` field with latest inputs +- Updates `submodule` field with slug (e.g., `fin_plate`) +- Verifies ownership via `user_email` + +## Migration Notes + +### From Old Endpoint +**Old (removed):** +``` +(Removed) POST /calculate-output/FinPlateConnection +Body: { "Module": "FinPlateConnection", ...inputs } +``` + +**New (current):** +``` +POST /api/modules/shear-connection/fin-plate/design/ +Body: { "inputs": {...} } or just {...inputs} +``` + +### Frontend Changes Needed +1. Update API endpoint URLs +2. Extract `inputs` into separate key if using new format +3. Add `project_id` to request if user is authenticated +4. Handle `project_saved` flag in response + +## Testing + +### Test Guest Mode +```python +# No auth header +response = client.post('/api/modules/shear-connection/fin-plate/design/', { + 'inputs': {...} +}) +assert response.status_code == 200 +assert response.json()['project_saved'] == False +``` + +### Test Authenticated with Project +```python +# With auth header +response = client.post('/api/modules/shear-connection/fin-plate/design/', { + 'inputs': {...}, + 'project_id': 123 +}, HTTP_AUTHORIZATION='Bearer ') +assert response.status_code == 200 +assert response.json()['project_saved'] == True +``` + +### Test Project Not Found +```python +response = client.post('/api/modules/shear-connection/fin-plate/design/', { + 'inputs': {...}, + 'project_id': 999 # Non-existent project +}, HTTP_AUTHORIZATION='Bearer ') +assert response.json()['project_saved'] == False +assert 'not found' in response.json()['project_error'].lower() +``` + diff --git a/documentation/backend/README.md b/documentation/backend/README.md new file mode 100644 index 000000000..7bf47468a --- /dev/null +++ b/documentation/backend/README.md @@ -0,0 +1,131 @@ +# Core Utilities + +Shared utilities for all Django apps in the project. + +## `module_helpers.py` + +Shared authentication, guest mode, and project saving logic for all module endpoints. + +### Functions + +#### `is_guest_user(request) -> bool` +Check if the current user is a guest user. + +```python +from apps.core.utils.module_helpers import is_guest_user + +if is_guest_user(request): + # Handle guest user +``` + +#### `get_user_email(request) -> Optional[str]` +Get user email from JWT token or user object. + +```python +from apps.core.utils.module_helpers import get_user_email + +user_email = get_user_email(request) +``` + +#### `save_to_project(project_id, user_email, inputs, submodule_slug, module_name=None) -> dict` +Save design inputs to a project in the database. + +```python +from apps.core.utils.module_helpers import save_to_project + +result = save_to_project( + project_id=123, + user_email='user@example.com', + inputs={'Bolt.Diameter': '16', ...}, + submodule_slug='fin-plate', + module_name='shear-connection' +) +# Returns: {'saved': True, 'project_id': 123} or {'saved': False, 'error': '...'} +``` + +#### `handle_design_request(request, inputs, project_id, submodule_slug, module_name=None) -> dict` +**Convenience function** that combines guest checking and project saving. + +```python +from apps.core.utils.module_helpers import handle_design_request + +context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug='fin-plate', + module_name='shear-connection' +) + +# context contains: +# { +# 'is_guest': bool, +# 'user_email': str or None, +# 'project_result': dict or None +# } +``` + +### Usage in ViewSets + +**Example for any parent module (shear_connection, moment_connection, etc.):** + +```python +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from apps.core.utils.module_helpers import handle_design_request +from .registry import MyModuleRegistry + +class MyModuleViewSet(viewsets.ViewSet): + permission_classes = [AllowAny] + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/design') + def design(self, request, submodule_slug=None): + # Get service from registry + ServiceClass = MyModuleRegistry.get_service_by_slug(submodule_slug) + if not ServiceClass: + return Response({'error': 'Module not found'}, status=404) + + # Extract inputs + inputs = request.data.get('inputs', request.data) + project_id = request.data.get('project_id') + + # Handle auth and project saving (shared logic) + context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug=submodule_slug, + module_name='my-module' # e.g., 'moment-connection' + ) + + try: + # Call service + result = ServiceClass.calculate( + inputs=inputs, + request=request, + project_id=project_id if not context['is_guest'] else None, + user_email=context['user_email'] + ) + + # Add project result to response + if context['project_result']: + result['project_saved'] = context['project_result']['saved'] + if context['project_result'].get('project_id'): + result['project_id'] = context['project_result']['project_id'] + if context['project_result'].get('error'): + result['project_error'] = context['project_result']['error'] + + return Response(result, status=200) + except Exception as e: + return Response({'error': str(e), 'success': False}, status=400) +``` + +### Benefits + +1. **DRY Principle**: No code duplication across modules +2. **Consistency**: All modules handle auth/projects the same way +3. **Maintainability**: Fix bugs or add features in one place +4. **Testability**: Test shared logic once, reuse everywhere + diff --git a/documentation/backend/SETUP_OSDAG_CORE.md b/documentation/backend/SETUP_OSDAG_CORE.md new file mode 100644 index 000000000..6b02ca3b2 --- /dev/null +++ b/documentation/backend/SETUP_OSDAG_CORE.md @@ -0,0 +1,102 @@ +# osdag_core Setup Guide + +## Status +✅ **osdag_core is configured for direct imports via sys.path** + +The `osdag_core` module is **not a pip-installable package** (no `setup.py` or `pyproject.toml`). Instead, it's configured to work via direct imports by adding the project root to Python's `sys.path` in Django settings. + +## How It Works + +The Django settings file (`backend/config/settings.py`) automatically adds the project root to `sys.path`, allowing direct imports: + +```python +from osdag_core.design_type.connection.fin_plate_connection import FinPlateConnection +``` + +**Configuration Location:** +- `backend/config/settings.py` (lines ~20-25) +- Automatically adds `BASE_DIR` (project root) to `sys.path` + +## Verification + +To verify `osdag_core` is accessible: + +```bash +# From backend/ directory +cd backend +python manage.py shell + +# In Django shell: +>>> from osdag_core.design_type.connection.fin_plate_connection import FinPlateConnection +>>> print("Import successful!") +``` + +## Troubleshooting + +### Error: "No module named 'osdag_core'" + +1. **Check Django settings:** + - Verify `backend/config/settings.py` has the `sys.path` configuration + - Look for: `sys.path.insert(0, str(BASE_DIR))` + +2. **Verify project structure:** + ``` + Osdag-web/ + ├── osdag_core/ ← Must exist at project root + │ ├── __init__.py ← Should exist (created automatically) + │ ├── design_type/ + │ └── ... + └── backend/ + └── config/ + └── settings.py ← Contains sys.path configuration + ``` + +3. **Restart Django server:** + ```bash + # Stop the server (Ctrl+C) and restart + python manage.py runserver + ``` + +### Error: "ModuleNotFoundError: No module named 'pylatex'" + +This is a **dependency issue**, not a path issue. Install missing dependencies: + +```bash +pip install pylatex +# Or install all requirements: +pip install -r requirements.txt +``` + +### Error persists + +1. **Check sys.path in Django shell:** + ```python + python manage.py shell + >>> import sys + >>> print(sys.path) + # Should include your project root directory + ``` + +2. **Manual verification:** + ```python + python manage.py shell + >>> import osdag_core + >>> print(osdag_core.__file__) + # Should print path to osdag_core/__init__.py + ``` + +## Why Not pip install? + +`osdag_core` doesn't have a `setup.py` or `pyproject.toml`, so it cannot be installed as a package. The direct import approach via `sys.path` is: +- ✅ **Simpler**: No package installation needed +- ✅ **Faster**: No build/install step +- ✅ **Flexible**: Works immediately after cloning the repo +- ✅ **Standard**: Common pattern for monorepo projects + +## Notes + +- **No installation needed**: Just ensure `osdag_core/` exists at project root +- **Automatic**: Django settings handle the path configuration +- **Development**: Changes to `osdag_core` are immediately available (no reinstall) +- **Production**: Same setup works in production (no special deployment steps) + diff --git a/documentation/backend/TEST_SHEAR_CONNECTION.md b/documentation/backend/TEST_SHEAR_CONNECTION.md new file mode 100644 index 000000000..212c2e628 --- /dev/null +++ b/documentation/backend/TEST_SHEAR_CONNECTION.md @@ -0,0 +1,238 @@ +# Testing Shear Connection Module + +## Quick Start + +### 1. Activate Conda Environment +```powershell +conda activate osdag-v2 +# or your Django environment +cd backend +``` + +### 2. Start Django Server +```powershell +python manage.py runserver +``` + +### 3. Test Endpoints + +## Available Endpoints + +### Fin Plate Connection +- **URL:** `POST http://localhost:8000/api/modules/shear-connection/fin-plate/design/` +- **Slug:** `fin-plate` + +### Cleat Angle Connection +- **URL:** `POST http://localhost:8000/api/modules/shear-connection/cleat-angle/design/` +- **Slug:** `cleat-angle` + +### End Plate Connection +- **URL:** `POST http://localhost:8000/api/modules/shear-connection/end-plate/design/` +- **Slug:** `end-plate` + +### Seated Angle Connection +- **URL:** `POST http://localhost:8000/api/modules/shear-connection/seated-angle/design/` +- **Slug:** `seated-angle` + +## Test with cURL (PowerShell) + +### Test 1: Guest Mode (No Auth) +```powershell +$body = @{ + "Bolt.Bolt_Hole_Type" = "Standard" + "Bolt.Diameter" = @("12", "16", "20") + "Bolt.Grade" = @("4.6", "4.8", "5.6") + "Bolt.Slip_Factor" = "0.3" + "Bolt.TensionType" = "Pre-tensioned" + "Bolt.Type" = "Friction Grip Bolt" + "Connectivity" = "Column Flange-Beam-Web" + "Connector.Material" = "E 250 (Fe 410 W)A" + "Design.Design_Method" = "Limit State Design" + "Detailing.Corrosive_Influences" = "No" + "Detailing.Edge_type" = "Rolled" + "Detailing.Gap" = "15" + "Load.Axial" = "50" + "Load.Shear" = "180" + "Material" = "E 250 (Fe 410 W)A" + "Member.Supported_Section.Designation" = "MB 350" + "Member.Supported_Section.Material" = "E 250 (Fe 410 W)A" + "Member.Supporting_Section.Designation" = "JB 150" + "Member.Supporting_Section.Material" = "E 250 (Fe 410 W)A" + "Module" = "FinPlateConnection" + "Weld.Fab" = "Shop Weld" + "Weld.Material_Grade_OverWrite" = "410" + "Connector.Plate.Thickness_List" = @("10", "12", "16", "18", "20") +} | ConvertTo-Json + +Invoke-RestMethod -Uri "http://localhost:8000/api/modules/shear-connection/fin-plate/design/" ` + -Method POST ` + -ContentType "application/json" ` + -Body $body +``` + +### Test 2: With Inputs Wrapper (Alternative Format) +```powershell +$body = @{ + "inputs" = @{ + "Bolt.Diameter" = "16" + "Bolt.Grade" = "8.8" + "Load.Shear" = "180" + "Member.Supported_Section.Designation" = "MB 350" + "Member.Supporting_Section.Designation" = "JB 150" + # ... add other required fields + } +} | ConvertTo-Json -Depth 10 + +Invoke-RestMethod -Uri "http://localhost:8000/api/modules/shear-connection/fin-plate/design/" ` + -Method POST ` + -ContentType "application/json" ` + -Body $body +``` + +### Test 3: Authenticated with Project ID +```powershell +$token = "YOUR_JWT_TOKEN_HERE" +$body = @{ + "inputs" = @{ + "Bolt.Diameter" = "16" + "Load.Shear" = "180" + # ... other inputs + } + "project_id" = 123 +} | ConvertTo-Json -Depth 10 + +$headers = @{ + "Authorization" = "Bearer $token" + "Content-Type" = "application/json" +} + +Invoke-RestMethod -Uri "http://localhost:8000/api/modules/shear-connection/fin-plate/design/" ` + -Method POST ` + -Headers $headers ` + -Body $body +``` + +## Test with Python (Django Shell) + +```python +# Start Django shell +python manage.py shell + +# In shell: +from django.test import Client +import json + +client = Client() + +# Test fin-plate endpoint +response = client.post( + '/api/modules/shear-connection/fin-plate/design/', + data=json.dumps({ + "Bolt.Diameter": "16", + "Bolt.Grade": "8.8", + "Load.Shear": "180", + "Member.Supported_Section.Designation": "MB 350", + "Member.Supporting_Section.Designation": "JB 150", + # ... add all required fields + }), + content_type='application/json' +) + +print(f"Status: {response.status_code}") +print(f"Response: {response.json()}") +``` + +## Test with Postman + +1. **Method:** POST +2. **URL:** `http://localhost:8000/api/modules/shear-connection/fin-plate/design/` +3. **Headers:** + - `Content-Type: application/json` + - `Authorization: Bearer ` (optional, for authenticated requests) +4. **Body (raw JSON):** +```json +{ + "Bolt.Diameter": "16", + "Bolt.Grade": "8.8", + "Load.Shear": "180", + "Member.Supported_Section.Designation": "MB 350", + "Member.Supporting_Section.Designation": "JB 150", + "Connectivity": "Column Flange-Beam-Web", + "Material": "E 250 (Fe 410 W)A", + "Module": "FinPlateConnection" +} +``` + +## Expected Responses + +### Success (200 OK) +```json +{ + "data": { + // Design calculation results + }, + "logs": [ + // Calculation logs + ], + "success": true +} +``` + +### Error (400 Bad Request) +```json +{ + "error": "Missing required key: Bolt.Diameter", + "success": false +} +``` + +### Not Found (404) +```json +{ + "error": "Sub-module invalid-slug not found" +} +``` + +## Verify Registry Auto-Discovery + +```python +# In Django shell +from apps.modules.shear_connection.registry import ShearConnectionRegistry + +# Check registered modules +print("Registered slugs:", list(ShearConnectionRegistry._registry.keys())) +print("Registered MODULE_IDs:", list(ShearConnectionRegistry._module_id_map.keys())) + +# Test lookup +service = ShearConnectionRegistry.get_service_by_slug('fin-plate') +print(f"Fin Plate Service: {service}") + +service = ShearConnectionRegistry.get_service_by_module_id('FinPlateConnection') +print(f"Fin Plate Service by MODULE_ID: {service}") +``` + +## Troubleshooting + +### Error: "Sub-module not found" +- Check that `__init__.py` exists in sub-module folder +- Verify `MODULE_ID` and `Service` are exported in `__init__.py` +- Check registry auto-discovery ran (see above) + +### Error: "ModuleNotFoundError: No module named 'osdag_core'" +- Ensure conda environment is activated +- Verify `osdag_core` is in `sys.path` (check `settings.py`) +- Restart Django server + +### Error: "Missing required key" +- Check adapter's `get_required_keys()` function +- Ensure all required fields are in request body +- See adapter file for full list of required keys + +## Next Steps + +After testing shear connections: +1. Test moment connections: `/api/modules/moment-connection/{submodule}/design/` +2. Compare results with old endpoints +3. Test project saving functionality +4. Test guest vs authenticated modes + diff --git a/documentation/backend/test_shear_connection.py b/documentation/backend/test_shear_connection.py new file mode 100644 index 000000000..463a1c5ca --- /dev/null +++ b/documentation/backend/test_shear_connection.py @@ -0,0 +1,139 @@ +""" +Quick test script for shear connection endpoints +Run: python test_shear_connection.py +""" +import os +import sys +import django + +# Setup Django +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') +django.setup() + +from django.test import Client +import json + +# Sample fin plate input data +FIN_PLATE_INPUTS = { + "Bolt.Bolt_Hole_Type": "Standard", + "Bolt.Diameter": ["12", "16", "20"], + "Bolt.Grade": ["4.6", "4.8", "5.6"], + "Bolt.Slip_Factor": "0.3", + "Bolt.TensionType": "Pre-tensioned", + "Bolt.Type": "Friction Grip Bolt", + "Connectivity": "Column Flange-Beam-Web", + "Connector.Material": "E 250 (Fe 410 W)A", + "Design.Design_Method": "Limit State Design", + "Detailing.Corrosive_Influences": "No", + "Detailing.Edge_type": "Rolled", + "Detailing.Gap": "15", + "Load.Axial": "50", + "Load.Shear": "180", + "Material": "E 250 (Fe 410 W)A", + "Member.Supported_Section.Designation": "MB 350", + "Member.Supported_Section.Material": "E 250 (Fe 410 W)A", + "Member.Supporting_Section.Designation": "JB 150", + "Member.Supporting_Section.Material": "E 250 (Fe 410 W)A", + "Module": "FinPlateConnection", + "Weld.Fab": "Shop Weld", + "Weld.Material_Grade_OverWrite": "410", + "Connector.Plate.Thickness_List": ["10", "12", "16", "18", "20"], +} + +def test_registry(): + """Test that registry auto-discovery worked""" + print("=" * 60) + print("Testing Registry Auto-Discovery") + print("=" * 60) + + from apps.modules.shear_connection.registry import ShearConnectionRegistry + + slugs = list(ShearConnectionRegistry._registry.keys()) + module_ids = list(ShearConnectionRegistry._module_id_map.keys()) + + print(f"✅ Registered slugs: {slugs}") + print(f"✅ Registered MODULE_IDs: {module_ids}") + + # Test lookup + service = ShearConnectionRegistry.get_service_by_slug('fin-plate') + if service: + print(f"✅ Fin Plate Service found: {service.__name__}") + else: + print("Fin Plate Service NOT found!") + return False + + return True + +def test_endpoint(submodule_slug, inputs): + """Test a shear connection endpoint""" + print(f"\n{'=' * 60}") + print(f"Testing: {submodule_slug}") + print(f"{'=' * 60}") + + client = Client() + url = f'/api/modules/shear-connection/{submodule_slug}/design/' + + try: + response = client.post( + url, + data=json.dumps(inputs), + content_type='application/json' + ) + + print(f"Status Code: {response.status_code}") + + if response.status_code == 200: + data = response.json() + print(f"✅ Success: {data.get('success', False)}") + if 'data' in data: + print(f"✅ Data keys: {list(data['data'].keys())[:5]}...") # First 5 keys + if 'logs' in data: + print(f"✅ Logs count: {len(data['logs'])}") + return True + else: + print(f"Error: {response.json()}") + return False + + except Exception as e: + print(f"Exception: {str(e)}") + import traceback + traceback.print_exc() + return False + +def main(): + print("\n" + "=" * 60) + print("SHEAR CONNECTION MODULE TEST") + print("=" * 60 + "\n") + + # Test 1: Registry + if not test_registry(): + print("\nRegistry test failed. Cannot continue.") + return + + # Test 2: Fin Plate endpoint + print("\n" + "-" * 60) + test_endpoint('fin-plate', FIN_PLATE_INPUTS) + + # Test 3: Invalid slug + print("\n" + "-" * 60) + print("Testing invalid slug...") + client = Client() + response = client.post( + '/api/modules/shear-connection/invalid-slug/design/', + data=json.dumps(FIN_PLATE_INPUTS), + content_type='application/json' + ) + print(f"Status Code: {response.status_code}") + if response.status_code == 404: + print("✅ Correctly returns 404 for invalid slug") + else: + print(f"Expected 404, got {response.status_code}") + + print("\n" + "=" * 60) + print("TEST COMPLETE") + print("=" * 60) + +if __name__ == '__main__': + main() + diff --git a/documentation/frontend/AUTHENTICATION_FLOW_ANALYSIS.md b/documentation/frontend/AUTHENTICATION_FLOW_ANALYSIS.md new file mode 100644 index 000000000..7bc249393 --- /dev/null +++ b/documentation/frontend/AUTHENTICATION_FLOW_ANALYSIS.md @@ -0,0 +1,329 @@ +# Authentication Flow Analysis + +## Overview +This document analyzes the complete authentication flow after the recent changes, including guest, email/password, and Google authentication, along with edge cases and non-guest features. + +--- + +## ✅ Authentication Flows + +### 1. Guest Authentication Flow + +**Frontend (`LoginPage.jsx`):** +1. User clicks "Continue as Guest" +2. `handleGuestSignIn()` called +3. Calls `userLogin("", "", true)` with empty email/password +4. Backend returns JWT token with `is_guest: true` (no email in token) +5. Frontend stores: + - `userType: "guest"` + - `username: "guest_xxx"` + - `access` token + - `refresh` token + - **NO email stored** ✅ + +**Backend (`user_view.py`):** +1. Receives `isGuest: true` in request +2. Generates guest username: `guest_xxx` +3. Creates JWT token with: + - `user_id: 0` + - `username: guest_xxx` + - `is_guest: true` + - **NO email** ✅ +4. Returns tokens and username (no email) + +**Auto-Login (`useAuth.js` + `utils/auth.js`):** +1. `checkAutoLogin()` checks `userType === 'guest'` first +2. Returns `{ isGuest: true }` without requiring email +3. `useAuth` hook sets `isLoggedIn(true)` directly (no API call) + +**Status:** ✅ Working correctly + +--- + +### 2. Email/Password Authentication Flow + +**Signup (`UserState.jsx`):** +1. User signs up with email/password +2. Backend creates account +3. **No automatic login** - user must login separately ✅ +4. `createJWTToken()` call removed (was redundant) + +**Login (`LoginPage.jsx` + `UserState.jsx`):** +1. User enters email/password +2. Calls `userLogin(email, password, false)` +3. Backend authenticates and returns JWT with email +4. Frontend stores: + - `userType: "user"` + - `email: user@example.com` + - `access` token + - `refresh` token + - `username` (if provided) + +**Auto-Login:** +1. `checkAutoLogin()` checks for valid token +2. Decodes token to get user info (including email) +3. Calls `userLogin(username, "", false, true)` with JWT login flag +4. Sets `isLoggedIn(true)` without password + +**Status:** ✅ Working correctly + +--- + +### 3. Google Authentication Flow + +**Login (`LoginPage.jsx`):** +1. User clicks "Log in with Google" +2. Firebase popup authenticates +3. Gets Firebase ID token +4. Sends to backend `/api/auth/firebase-login/` +5. Backend: + - Verifies Firebase token + - Auto-creates user if doesn't exist ✅ + - Returns JWT tokens +6. Frontend stores: + - `userType: "user"` + - `email: user@gmail.com` + - `access` token + - `refresh` token + - `username` + +**Status:** ✅ Working correctly + +--- + +## 🔒 Guest Restrictions + +### Backend Protection + +**Project API (`project_api.py`):** +- ✅ Line 69-70: Checks `is_guest` flag and returns 403 for guests +- ✅ Line 87: Tries to get email from JWT (guests blocked before this) +- ✅ Safe: Guests cannot create or list projects + +**Module Helpers (`module_helpers.py`):** +- ✅ Line 121: `get_user_email()` only called when `not is_guest` +- ✅ Line 131-136: Project saving blocked for guests +- ✅ Safe: Guest operations don't require email + +**OSI API (`osi_api.py`):** +- ✅ Line 36-41: Detects guests +- ✅ Line 44: Guests get base64 download (no DB save) +- ✅ Safe: Guests can download but not save + +### Frontend Protection + +**ModulesCardLayout (`ModulesCardLayout.jsx`):** +- ✅ Line 46-49: Checks `isGuestUser()` before showing project modal +- ✅ Guests skip project creation UI entirely +- ⚠️ Line 73: Calls `getCurrentUserEmail()` but guests can't reach this code + +**EngineeringModule (`EngineeringModule.jsx`):** +- ✅ Line 308: Checks `isGuestUser()` for project saving +- ✅ Line 445: Blocks project operations for guests +- ✅ Safe: Guest restrictions enforced + +**Status:** ✅ Guest restrictions properly enforced + +--- + +## ⚠️ Potential Issues Found + +### Issue 1: Frontend Project Creation Email Check + +**Location:** `ModulesCardLayout.jsx:73` + +**Problem:** +```javascript +user_email: getCurrentUserEmail(), // Returns empty string for guests +``` + +**Analysis:** +- Guests are blocked at line 46-49, so this code path is unreachable for guests +- However, if somehow a guest reaches this point, empty email would be sent +- Backend will reject it anyway (line 69-70 or 88-89) + +**Recommendation:** +- Add explicit check: `if (isGuestUser()) return;` before line 61 +- Or: `user_email: isGuestUser() ? null : getCurrentUserEmail()` + +**Severity:** Low (defense in depth) + +--- + +### Issue 2: Backend get_user_email() for Guests + +**Location:** `module_helpers.py:41` + +**Problem:** +```python +user_email = request.auth.get('email') # Returns None for guests +``` + +**Analysis:** +- This function is only called when `not is_guest` (line 121) +- For guests, `get_user_email()` returns `None` (correct behavior) +- All callers check `is_guest` first before calling this + +**Status:** ✅ Safe (guards in place) + +--- + +### Issue 3: Token Refresh for Guests + +**Location:** `UserState.jsx:refreshJWTToken()` + +**Analysis:** +- Guest tokens can be refreshed +- New token will still have `is_guest: true` and no email +- Should work correctly + +**Status:** ✅ Should work (needs testing) + +--- + +### Issue 4: Auto-Login Edge Cases + +**Scenario 1: Guest with expired token** +- `checkAutoLogin()` checks `userType === 'guest'` first +- Returns guest user data even if token expired +- ✅ Works correctly + +**Scenario 2: Regular user with expired token** +- `checkAutoLogin()` checks token validity +- If invalid, clears tokens and returns `null` +- ✅ Works correctly + +**Scenario 3: Mixed state (guest userType but valid token)** +- `checkAutoLogin()` checks `userType` first +- If `userType === 'guest'`, returns guest (ignores token) +- ⚠️ Edge case: If user was guest, then logged in as regular user, but `userType` wasn't updated +- **Recommendation:** Ensure `userType` is always updated on login + +**Status:** ⚠️ Minor edge case (should be handled by proper logout) + +--- + +## 🧪 Testing Checklist + +### Guest Flow +- [ ] Guest login works +- [ ] No email in localStorage for guests +- [ ] No email in JWT token for guests +- [ ] Guest can access modules +- [ ] Guest cannot create projects (UI blocked) +- [ ] Guest cannot create projects (backend 403) +- [ ] Guest can download OSI files +- [ ] Guest cannot save to database +- [ ] Guest auto-login works on page refresh +- [ ] Guest logout clears all data + +### Email/Password Flow +- [ ] Signup creates account +- [ ] Signup doesn't auto-login (user must login) +- [ ] Login works with email/password +- [ ] Email stored in localStorage +- [ ] Token refresh works +- [ ] Auto-login works on page refresh +- [ ] Logout clears all data + +### Google Auth Flow +- [ ] Google login works +- [ ] Auto-creates user if doesn't exist +- [ ] Email stored correctly +- [ ] Tokens stored correctly +- [ ] Auto-login works on page refresh + +### Edge Cases +- [ ] Logout clears guest state +- [ ] Switching from guest to regular user works +- [ ] Switching from regular user to guest works +- [ ] Expired token handling +- [ ] Token refresh for guests +- [ ] Project creation blocked for guests (frontend + backend) +- [ ] Email-dependent features blocked for guests + +--- + +## 🔧 Recommended Fixes + +### Fix 1: Add Explicit Guest Check in Project Creation + +**File:** `frontend/src/homepage/components/ModulesCardLayout.jsx` + +**Change:** +```javascript +const handleProjectModalConfirm = async (projectName) => { + if (!selectedModule) return; + + // Explicit guest check (defense in depth) + if (isGuestUser()) { + console.warn("Guest users cannot create projects"); + return; + } + + const safeProjectName = (projectName || `${selectedModule.label} Project`).replace(/\s+/g, "_"); + // ... rest of function +}; +``` + +**Priority:** Low (defense in depth) + +--- + +### Fix 2: Ensure userType is Always Updated + +**File:** `frontend/src/context/UserState.jsx` + +**Check:** Ensure `userType` is set correctly on all login paths: +- ✅ Regular login: Sets `userType: "user"` (line 297) +- ✅ Guest login: Sets `userType: "guest"` (line 308) +- ✅ Google login: Sets `userType: "user"` (line 369 in LoginPage.jsx) +- ✅ JWT login: Should preserve existing `userType` or set based on token + +**Status:** ✅ Already handled correctly + +--- + +## 📊 Summary + +### ✅ Working Correctly +1. Guest authentication (no email needed) +2. Email/password authentication +3. Google authentication +4. Guest restrictions (frontend + backend) +5. Auto-login for all user types +6. Logout clears all data +7. Token refresh (needs testing for guests) + +### ⚠️ Minor Issues +1. Project creation could add explicit guest check (defense in depth) +2. Edge case: Mixed state if logout doesn't clear userType properly + +### 🎯 Overall Status +**The authentication flow is working correctly with proper guest restrictions in place. The removal of email from guest JWT tokens is safe and working as intended.** + +--- + +## 🔍 Code Locations + +### Frontend +- Guest login: `LoginPage.jsx:317-340` +- Regular login: `LoginPage.jsx:handleSubmit()` + `UserState.jsx:userLogin()` +- Google login: `LoginPage.jsx:344-396` +- Auto-login: `useAuth.js:17-30` + `utils/auth.js:checkAutoLogin()` +- Guest check: `utils/auth.js:isGuestUser()` +- Logout: `useAuth.js:33-38` + `utils/auth.js:clearTokens()` + +### Backend +- Guest login: `user_view.py:275-294` +- Regular login: `user_view.py:296-356` +- Google login: `views.py:FirebaseLoginView` +- Guest check: `module_helpers.py:is_guest_user()` +- Project protection: `project_api.py:68-70` +- Email extraction: `module_helpers.py:get_user_email()` + +--- + +**Last Updated:** After authentication flow fixes implementation +**Status:** ✅ Ready for testing + diff --git a/documentation/frontend/CREATE_NEW_MODULE.md b/documentation/frontend/CREATE_NEW_MODULE.md new file mode 100644 index 000000000..cd67d354a --- /dev/null +++ b/documentation/frontend/CREATE_NEW_MODULE.md @@ -0,0 +1,220 @@ +## Create a new Shear Connection module (reference: Fin Plate) + +This guide shows how to add a new module under `modules/shearConnection/` that works with the simplified context, the shared Engineering Module, and the auto CAD flow. + +### 1) Folder layout + +Create the following structure: + +``` +src/modules/shearConnection// + .jsx + components/ + OutputDock.jsx + configs/ + .js + .js +``` + +Use Fin Plate as a reference: + +``` +src/modules/shearConnection/finPlate/ + FinPlate.jsx + components/FinPlateOutputDock.jsx + configs/finPlateConfig.js + configs/finPlateOutputConfig.js +``` + +### 2) Keys and constants + +If needed, add module keys in `src/constants/DesignKeys.js`: + +```js +export const MODULE_KEY_ = ''; +export const MODULE_DISPLAY_ = ''; +``` + +These are used by config and routing (`designType`, `sessionName`). + +### 3) Module component + +Create `src/modules/shearConnection//.jsx`: + +```jsx +import React from "react"; +import { EngineeringModule } from "../../shared/components/EngineeringModule"; +import OutputDock from "./components/OutputDock"; +import { menuItems } from "../../shared/utils/moduleUtils"; +import { } from "./configs/"; +import { UI_STRINGS } from '../../../constants/UIStrings'; + +function () { + return ( + } + OutputDockComponent={OutputDock} + menuItems={menuItems} + title={UI_STRINGS.CONNECTING_MEMBERS} + /> + ); +} + +export default ; +``` + +### 4) Module config (inputs and submission) + +Create `src/modules/shearConnection//configs/.js`. Keys must match API expectations. + +```js +import { UI_STRINGS } from '../../../../constants/UIStrings'; +import { MODULE_KEY_, MODULE_DISPLAY_ } from '../../../../constants/DesignKeys'; + +export const = { + sessionName: MODULE_DISPLAY_, + routePath: "/design/connections/shear/", + designType: MODULE_KEY_, + cameraKey: "", + cadOptions: ["Model", "Beam", "Column"], + + defaultInputs: { + module: MODULE_KEY_, + // add stable defaults similar to Fin Plate + }, + + inputSections: [ + { + title: UI_STRINGS.CONNECTING_MEMBERS, + fields: [ + { key: "connectivity", label: UI_STRINGS.CONNECTIVITY, type: "connectivitySelect" }, + { key: "column_section", label: UI_STRINGS.COLUMN_SECTION, type: "select", options: "columnList", + conditionalDisplay: (extra) => extra?.selectedOption?.includes("Column") }, + { key: "beam_section", label: UI_STRINGS.BEAM_SECTION, type: "select", options: "beamList" }, + { key: "connector_material", label: UI_STRINGS.MATERIAL, type: "select", options: "materialList", + onChange: (value, inputs, setInputs, materialList) => { + const m = materialList.find(x => x.Grade === value); + setInputs({ ...inputs, connector_material: m?.Grade || value }); + } + }, + ] + }, + { + title: UI_STRINGS.FASTENERS, + fields: [ + { key: "bolt_diameter", label: UI_STRINGS.BOLT_DIAMETER, type: "customizable", + selectionKey: "bolt_diameter_select", modalKey: "boltDiameter" }, + { key: "bolt_grade", label: UI_STRINGS.BOLT_GRADE, type: "customizable", + selectionKey: "bolt_grade_select", modalKey: "propertyClass" }, + ] + }, + ], + + selectionConfig: [ + { key: "bolt_diameter_select", inputKey: "bolt_diameter", defaultValue: "All" }, + { key: "bolt_grade_select", inputKey: "bolt_grade", defaultValue: "All" }, + ], + + modalConfig: [ + { key: "boltDiameter", inputKey: "bolt_diameter", dataSource: "boltDiameterList" }, + { key: "propertyClass", inputKey: "bolt_grade", dataSource: "propertyClassList" }, + ], + + validateInputs: (inputs) => ({ isValid: true }), + + buildSubmissionParams: (inputs, allSelected, lists, extraState) => { + const connectivity = extraState?.selectedOption || inputs.connectivity; + return { + "Module": MODULE_KEY_, + "Connectivity": connectivity, + "Bolt.Diameter": allSelected.bolt_diameter ? lists.boltDiameterList : inputs.bolt_diameter, + "Bolt.Grade": allSelected.bolt_grade ? lists.propertyClassList : inputs.bolt_grade, + // map other inputs → backend keys + }; + }, +}; +``` + +Notes: +- `options: "beamList" | "columnList" | "materialList" | "boltDiameterList" | "thicknessList" | "propertyClassList"` automatically binds to context lists populated by `/populate`. +- “All/Customized” is supported for bolt/thickness/grade (multiple select vs full-list). + +### 5) Output Dock + +Create `src/modules/shearConnection//components/OutputDock.jsx`: + +```jsx +import React from "react"; +import { BaseOutputDock } from "../../../shared/components/BaseOutputDock"; +import { } from "../configs/"; +import { UI_STRINGS } from '../../../../constants/UIStrings'; + +const OutputDock = ({ output, extraState }) => { + return ( + } + title={UI_STRINGS.OUTPUT_DOCK} + extraState={extraState} + /> + ); +}; + +export default OutputDock; +``` + +Create `src/modules/shearConnection//configs/.js`: + +```js +export const = { + sections: { + "Bolt": [ + { key: "Bolt.Diameter", label: "Diameter (mm)" }, + { key: "Bolt.Grade_Provided", label: "Property Class" }, + { key: "Bolt.Shear", label: "Shear Capacity (kN)" }, + ], + "Plate": [ + { key: "Plate.Thickness", label: "Thickness (mm)" }, + { key: "Plate.Length", label: "Length (mm)" }, + ], + }, + modals: { + spacing: { type: "spacing", buttonText: "Open Spacing" }, + }, + modalTypes: { + spacing: { layout: "two-column", title: "Spacing", width: "60%" }, + }, + modalData: { + spacing: { + spacing: [ + { key: "Bolt.Pitch", label: "Pitch Distance (mm)" }, + { key: "Bolt.Gauge", label: "Gauge Distance (mm)" }, + ] + } + } +}; +``` + +`BaseOutputDock` automatically normalizes `output` whether it arrives as `{ data: {...} }` or a flat object, and reads values from `output[key].val` when present. + +### 6) Routing + +Wire the route in the app router to render `` at the same `routePath` used in your config. + +### 7) Data flow expected + +1. Populate on mount: the hook calls `getModuleData(designType)`, reducer sets lists (materials, bolts, thickness, grades, connectivity, beam/column). +2. Inputs: `InputSection` binds to lists by `options: ""`; All/Customized supported for arrays. +3. Design click: the hook validates, builds submission params, calls the module design endpoint under `api/modules/.../design/`, mirrors output to Output Dock, then auto-calls the module CAD endpoint under `api/modules/.../cad/` to render the 3D model. + +### 8) Key mapping tips + +- Output keys in your `outputConfig` must exactly match backend keys, e.g. `"Bolt.Diameter"`, `"Plate.Thickness"`. +- For materials, store `Grade` in inputs; backend expects the human-readable grade string. +- For All/Customized: when “All”, pass the full list from context (`lists.*`); when “Customized”, pass the user-selected array (`inputs.`). + +### 9) Troubleshooting + +- Lists empty in UI: check the module options endpoint under `api/modules/.../options/` runs once, `SET_ALL_MODULE_DATA` in reducer sets all lists, and `EngineeringModule.jsx` passes `contextData` to `InputSection`. +- Dropdowns empty: verify `InputSection` field `options` references a known list name; material uses `Grade` as value. +- Output empty: confirm the module `.../design/` endpoint returns `{ data: {...}, logs: [...] }` and the hook normalization is active; `BaseOutputDock` logs `normalizedOutput` keys in console. diff --git a/documentation/frontend/FRONTEND_IMPROVEMENTS.md b/documentation/frontend/FRONTEND_IMPROVEMENTS.md new file mode 100644 index 000000000..70c5db116 --- /dev/null +++ b/documentation/frontend/FRONTEND_IMPROVEMENTS.md @@ -0,0 +1,170 @@ +## Osdag Web Client – Improvements Roadmap + +This document lists potential improvements for the `frontend` frontend, with **priority** and **difficulty** levels to help plan work. + +### Legend +- **Priority**: P0 (critical), P1 (high), P2 (medium), P3 (low) +- **Difficulty**: E (easy), M (medium), H (hard) + +--- + +### 1. Authentication & Token Handling + +- **Unify token storage and helpers** + - **Notes**: `UserState.jsx` uses raw `localStorage` keys (`access`, `refresh`, `userType`, etc.) while `utils/auth.js` defines helpers (`setTokens`, `getAccessToken`, `clearTokens`, `isAuthenticated`, etc.). Centralizing on the helper functions will reduce duplication and subtle bugs. + - **Priority**: P1 + - **Difficulty**: M + +- **Fix minor inconsistencies in token naming** + - **Notes**: `refreshJWTToken` sets `localStorage.setItem("access", jsonResponse.access_token);` but response is logged as `jsonResponse.access`. Ensure consistent property names and use `setTokens` to avoid typos. + - **Priority**: P1 + - **Difficulty**: E + +- **Reduce verbose console logging for auth flows** + - **Notes**: `UserState.jsx` logs sensitive-ish data (e.g. tokens, passwords in dev) and very verbose messages. Replace with guarded, structured logging or a dedicated logger, and avoid logging passwords entirely. + - **Priority**: P1 + - **Difficulty**: E + +--- + +### 2. Global & Module Contexts + +- **Avoid mutating `initialValue` in `GlobalState.jsx`** + - **Notes**: `getDesignTypes` writes to `initialValue.fetch_cache`. This is a shared constant and can lead to subtle bugs if multiple providers are ever created. Move cache into reducer state or a `useRef`. + - **Priority**: P1 + - **Difficulty**: M + +- **Remove or gate noisy debug logs in `ModuleState.jsx`** + - **Notes**: `createCADModel` logs many debug lines (`[cadissue] ...`) which are helpful in development but noisy in production. Wrap them behind a debug flag or remove them. + - **Priority**: P2 + - **Difficulty**: E + +- **Gradually remove deprecated legacy functions in `ModuleState.jsx`** + - **Notes**: There is a large compatibility section with deprecated functions wrapped around the new 8-core API. Plan a deprecation strategy: find all call sites, migrate to the new API, then delete the wrappers. + - **Priority**: P2 + - **Difficulty**: H (requires cross-module refactor) + +--- + +### 3. `BaseInputDock` UX & Implementation + +- **Replace direct DOM tooltip creation with React-based tooltip** + - **Notes**: The lock overlay click handler creates and manages a tooltip using `document.createElement` and manual DOM operations. This bypasses React and can be harder to maintain. Implement a small React tooltip component or use a UI library instead. + - **Priority**: P2 + - **Difficulty**: M + +- **Extract lock-overlay logic into a reusable hook or component** + - **Notes**: The overlay handles zoom animation, tooltip positioning, and click blocking in one inline callback. Extracting this to a hook/component will improve testability and readability. + - **Priority**: P3 + - **Difficulty**: M + +- **Use stable keys for `InputSection` instead of `index`** + - **Notes**: Currently `key={index}` is used when mapping `moduleConfig.inputSections`. Prefer a stable identifier from `section` (e.g. `section.id` or `section.name`) to avoid unnecessary remounts and state issues when the list changes. + - **Priority**: P2 + - **Difficulty**: E + +--- + +### 4. Routing & Entry Points + +- **Remove unused `renderedOnce` from `App.jsx`** + - **Notes**: The variable `let renderedOnce = false;` is declared but not used. Clean it up to avoid confusion. + - **Priority**: P3 + - **Difficulty**: E + +- **Add route-level error boundaries** + - **Notes**: Current router setup has no errorElement / error boundary for routes. Adding error boundaries improves resilience when a module fails to render. + - **Priority**: P2 + - **Difficulty**: M + +--- + +### 5. API & Network Layer + +- **Normalize API error handling** + - **Notes**: Different contexts use slightly different patterns (some `console.error`, some dispatch error messages, some silent). Introduce a shared error helper (e.g. `handleApiError`) and consistent error messages surfaced to the UI. + - **Priority**: P1 + - **Difficulty**: M + +- **Consider moving repeated `fetch`/`axios` logic into a thin API client** + - **Notes**: Many files manually construct URLs (`${BASE_URL}api/...`) with similar options (`mode: "cors", credentials: "include"`). A shared client wrapper would reduce duplication and centralize cross-cutting concerns (auth headers, retries, logging). + - **Priority**: P2 + - **Difficulty**: M + +--- + +### 6. Type Safety & Maintainability + +- **Adopt JSDoc or TypeScript gradually for complex contexts** + - **Notes**: Contexts like `ModuleState.jsx` and `UserState.jsx` expose many functions and state fields. Adding lightweight typing (JSDoc now, TS later) will make it easier to work safely on these modules. + - **Priority**: P3 + - **Difficulty**: H (if converting to TS), M (if only adding JSDoc) + +- **Document public context APIs** + - **Notes**: Add README-style docs or inline comments for the 8-core module API and user context thunks so new contributors know which functions are stable and which are deprecated. + - **Priority**: P2 + - **Difficulty**: E + +--- + +### 7. General Code Hygiene + +- **Run eslint and clean up unused variables/imports** + - **Notes**: There are a few unused variables (e.g. `renderedOnce`), and likely more across modules. Regular linting keeps the codebase tidy and reduces dead code. + - **Priority**: P2 + - **Difficulty**: E + +- **Standardize logging style** + - **Notes**: Logging is a mix of plain `console.log`, prefixed tags, and verbose text. Decide on a standard (e.g. `[User]`, `[Module]`, `[CAD]`) and keep logs concise; consider stripping logs in production builds. + - **Priority**: P3 + - **Difficulty**: E + +--- + +### 8. Keys, Slugs & Centralized UI Strings + +- **Use centralized UI strings for module and connection labels** + - **Notes**: Labels like "Simple Connections", "Shear Connection", "Fin Plate", "End Plate" etc. are hardcoded in multiple places (e.g. `constants/modules.js`, homepage components). These should come from `UI_STRINGS` to ensure consistency and make global renames easy. Initial refactor done in `constants/modules.js`; remaining call sites can be migrated gradually. + - **Priority**: P1 + - **Difficulty**: M + +- **Align module keys, slugs, and routes** + - **Notes**: There are three layers for module identity: module keys in `DesignKeys.js`, API slugs in `apiRoutes.js` (`MODULE_SLUGS`), and route paths in `constants/modules.js` and `App.jsx`. Long term, use module keys as the single source of truth, derive slugs via `getModuleSlug`, and keep all route patterns in `MODULE_ROUTES` to avoid divergent hardcoded paths. + - **Priority**: P2 + - **Difficulty**: H + +--- + +### 9. State Management & Cleanup ✅ COMPLETED + +- **✅ Fixed: ModuleContext state persistence across module switches** + - **Status**: Fixed - Added module change detection in `EngineeringModule.jsx` that clears `ModuleContext` state when switching between modules + - **Implementation**: Added `prevModuleRef` to track module changes and clear design/output/CAD state automatically + - **Files Changed**: `EngineeringModule.jsx`, `useEngineeringModule.js`, `ModuleReducer.jsx` + +- **✅ Fixed: No cleanup on component unmount** + - **Status**: Fixed - Added cleanup effect that clears `ModuleContext` state when `EngineeringModule` unmounts + - **Implementation**: Added `useEffect` cleanup function that calls `resetModuleState()` on unmount + +- **✅ Fixed: Project loading doesn't clear previous design state** + - **Status**: Fixed - Clear design state before loading project inputs to prevent showing stale results + - **Implementation**: Modified project loading effect to call `resetModuleState()` and `clearDesignResults()` before loading project data + +- **✅ Fixed: RESET_MODULE_STATE doesn't clear hoverDict** + - **Status**: Fixed - Updated `RESET_MODULE_STATE` reducer action to also clear `hoverDict` (CAD hover tooltips) + +- **✅ Fixed: Browser back/forward navigation state persistence** + - **Status**: Fixed - Module change detection automatically handles route changes (including browser navigation) and clears state + +**Edge Cases Now Handled:** +- ✅ Same tab, different module → State cleared automatically +- ✅ Same tab, same module, different project → State cleared automatically +- ✅ Same tab, same module, new design → State cleared before new design +- ✅ New tab → Works correctly (separate React instance) +- ✅ Browser refresh → Works correctly (fresh state) +- ✅ Browser back/forward → State cleared on module change +- ✅ Direct URL navigation → State cleared on mount if module changed +- ✅ Guest vs authenticated user → State cleared for both user types +- ✅ Component unmount → State cleared on unmount + + diff --git a/documentation/frontend/README.md b/documentation/frontend/README.md new file mode 100644 index 000000000..384306c8d --- /dev/null +++ b/documentation/frontend/README.md @@ -0,0 +1,170 @@ +# Simply Supported Beam Module + +## Overview +This module implements the Simply Supported Beam (Flexural Member) design following the same pattern as the Tension Member (BoltedToEnd) module. + +## Structure +``` +simplySupportedBeam/ +├── SimplySupportedBeam.jsx # Main component using EngineeringModule +├── components/ +│ └── SimplySupportedBeamOutputDock.jsx # Output dock component +├── configs/ +│ ├── simplySupportedBeamConfig.js # Input configuration +│ └── simplySupportedBeamOutputConfig.js # Output configuration +├── index.js # Module export +└── README.md # This documentation +``` + +## Implementation Details + +### 1. Main Component (SimplySupportedBeam.jsx) +- Uses the shared `EngineeringModule` component +- Follows the same pattern as `BoltedToEnd.jsx` +- Integrates with the module system using `menuItems` and configurations + +### 2. Input Configuration (simplySupportedBeamConfig.js) +Based on backend API requirements from `simply_supported_beam.py`: + +#### Required Backend Keys: +- `Module`: "Simply-Supported-Beam" +- `Member.Profile`: Section profile (Beams/Columns) +- `Member.Designation`: Section designation list +- `Material`: Material grade +- `Member.Material`: Section material +- `Design.Design_Method`: Design method +- `Design.Allowable_Class`: Allowable section class +- `Design.Effective_Area_Parameter`: Effective area parameter +- `Design.Length_Overwrite`: Length overwrite factor +- `Design.Bearing_Length`: Bearing length at supports +- `Load.Shear`: Shear force +- `Load.Moment`: Bending moment +- `Member.Length`: Member length +- `Support.Type`: Support type (Laterally Supported/Unsupported) +- `Torsional.Restraint`: Torsional restraint (Fixed/Free) +- `Warping.Restraint`: Warping restraint (Fixed/Free) + +#### Input Sections: +1. **Member Properties**: Section profile, designation, material, length +2. **Loads**: Shear force, bending moment +3. **Design Parameters**: Design method, allowable class, support type, restraints +4. **Advanced Parameters**: Effective area parameter, length overwrite, bearing length + +### 3. Output Configuration (simplySupportedBeamOutputConfig.js) +Based on expected output from flexure design calculations: + +#### Output Sections: +1. **Section**: Optimum designation, dimensions, properties +2. **Section Classification**: Section class, Beta_b, classification +3. **Strength**: Plastic strength, bending strength, LTB strength +4. **Lateral Torsional Buckling**: Critical moment, torsional/warping constants +5. **Web Buckling**: Buckling strength, crippling strength +6. **Design Status**: Utilization ratio, design status + +#### Modal Details: +- **Strength Modal**: Detailed strength calculations +- **LTB Modal**: Lateral torsional buckling analysis +- **Web Buckling Modal**: Web buckling and crippling analysis + +### 4. Output Dock Component +- Uses `BaseOutputDock` component +- Structured output display with modals +- Follows the same pattern as other modules + +## Constants Added +Added the following constants to `DesignKeys.js`: + +### Flexural Member Constants: +- `KEY_ALLOW_CLASS` +- `KEY_EFFECTIVE_AREA_PARA` +- `KEY_LENGTH_OVERWRITE` +- `KEY_BEARING_LENGTH` +- `KEY_SUPPORT` +- `KEY_TORSIONAL_RES` +- `KEY_WARPING_RES` + +### Display Constants: +- `KEY_DISP_PLASTIC_STRENGTH_MOMENT` +- `KEY_DISP_Bending_STRENGTH_MOMENT` +- `KEY_DISP_LTB_Bending_STRENGTH_MOMENT` +- `KEY_DISP_Elastic_CM` +- `KEY_DISP_T_constatnt` +- `KEY_DISP_W_constatnt` +- `KEY_DISP_BUCKLING_STRENGTH` +- `KEY_WEB_CRIPPLING` +- `KEY_IMPERFECTION_FACTOR_LTB` +- `KEY_SR_FACTOR_LTB` +- `KEY_NON_DIM_ESR_LTB` + +## Backend Integration + +### API Endpoint +The module connects to the backend through: +- **API Module**: `osdag_api/modules/simply_supported_beam.py` +- **Design Type**: `design_type/flexural_member/flexure.py` + +### Backend Functions: +- `get_required_keys()`: Returns required input keys +- `validate_input()`: Validates input parameters +- `create_from_input()`: Creates Flexure module instance +- `generate_output()`: Generates design output +- `create_cad_model()`: Creates 3D CAD model + +### Key Mapping: +The frontend keys are mapped to backend keys in `buildSubmissionParams()` function. + +## Routing +- **Route**: `/design/:designType/simply_supported_beam` +- **Component**: `SimplySupportedBeam` +- **Integration**: Added to `App.jsx` and `Window.jsx` + +## CAD Integration +- **Camera Key**: "FlexuralMember" +- **CAD Options**: ["Model", "Beam"] +- **3D Model**: Supports beam visualization + +## Testing Status +✅ **Build**: Successfully compiles without errors +✅ **Routing**: Route added and configured +✅ **Integration**: Uses shared components and patterns +✅ **Constants**: All required constants added + +## Next Steps + +### Backend Requirements: +1. **Ensure Backend API**: Verify `osdag_api/modules/simply_supported_beam.py` is complete +2. **Test Backend Integration**: Test the API endpoints +3. **CAD Model Support**: Ensure 3D model generation works +4. **Output Validation**: Verify output keys match frontend expectations + +### Frontend Enhancements: +1. **Error Handling**: Add proper error messages for validation +2. **Tooltips**: Add helpful tooltips for advanced parameters +3. **Unit Tests**: Add unit tests for configurations +4. **Documentation**: Add user documentation + +### Design Validation: +1. **Input Validation**: Test all input validation scenarios +2. **Output Display**: Verify output formatting and modals +3. **CAD Visualization**: Test 3D model display +4. **Design Report**: Ensure design report generation works + +## Usage Example +```javascript +// Navigation to module +navigate('/design/flexure/simply_supported_beam'); + +// Component will render with: +// - Input form with 4 sections +// - 3D CAD viewer +// - Output dock with organized results +// - Design report capability +// - Save/load functionality +``` + +## Dependencies +- React 18+ +- Ant Design components +- Three.js for 3D visualization +- Shared engineering module components +- Backend Python API integration \ No newline at end of file diff --git a/documentation/frontend/REFACTORED_HOOK_SUMMARY.md b/documentation/frontend/REFACTORED_HOOK_SUMMARY.md new file mode 100644 index 000000000..33813b5c3 --- /dev/null +++ b/documentation/frontend/REFACTORED_HOOK_SUMMARY.md @@ -0,0 +1,196 @@ +# useEngineeringModule Hook Refactoring Summary + +## 🎯 Overview +The `useEngineeringModule` hook has been successfully refactored to use the new simplified ModuleContext API, improving performance, maintainability, and error handling. + +## ✅ Key Improvements + +### 1. **Simplified Context API Usage** +**Before:** +```javascript +const { + getSupportedData, + getDesingPrefData, + createDesignReport, + // ... 25+ other legacy functions +} = useContext(ModuleContext); +``` + +**After:** +```javascript +const { + // NEW SIMPLIFIED API - 8 Core Functions + getModuleData, // Universal data fetcher + manageDesignPreferences, // Design preferences management + createDesign, // Design calculation + generateReport, // Unified report generation + resetModuleState, // State reset +} = useContext(ModuleContext); +``` + +### 2. **Enhanced Error Handling** +**Before:** +```javascript +// Old way - no error handling +try { + getModuleData(moduleConfig.designType); +} catch (error) { + console.error('Error:', error); +} +``` + +**After:** +```javascript +// New way - comprehensive error handling with result checking +const result = await getModuleData(moduleConfig.designType); + +if (result && result.success) { + console.log('✅ Module data loaded successfully'); + console.log('✅ Data keys:', Object.keys(result.data || {})); +} else { + console.error('Failed to load module data:', result?.error || 'Unknown error'); +} +``` + +### 3. **Async/Await Pattern** +**Before:** +```javascript +// Synchronous calls with no feedback +getSupportedData({ + supported_section: inputs.member_designation, +}); +``` + +**After:** +```javascript +// Proper async handling with status feedback +const result = await manageDesignPreferences('get', { + supported_section: inputs.member_designation, +}); + +if (result && result.success) { + console.log('✅ Supported data loaded successfully'); +} else { + console.error('Failed to load supported data:', result?.error); +} +``` + +### 4. **Unified Report Generation** +**Before:** +```javascript +// Direct function call with no error handling +createDesignReport(designReportInputs); +``` + +**After:** +```javascript +// Unified API with comprehensive error handling +const result = await generateReport('design_report', { + ...designReportInputs, + moduleId: moduleConfig.designType, + inputValues: inputs, + designStatus: true, + logs: logs || [], +}); + +if (result && result.success) { + console.log('✅ Design report generated successfully'); +} else { + console.error('Failed to generate design report:', result?.error); + alert(`Failed to generate design report: ${result?.error || 'Unknown error'}`); +} +``` + +## Technical Changes + +### Module Data Loading +- ✅ **Replaced** multiple specific data fetchers with single `getModuleData()` +- ✅ **Added** comprehensive logging with emoji indicators +- ✅ **Enhanced** error handling with result validation +- ✅ **Improved** performance with single API call + +### Design Preferences Management +- ✅ **Consolidated** `getSupportedData()` and `getDesingPrefData()` into `manageDesignPreferences()` +- ✅ **Added** action-based parameter structure +- ✅ **Enhanced** error handling and logging +- ✅ **Maintained** backward compatibility + +### Report Generation +- ✅ **Unified** report generation under `generateReport()` +- ✅ **Added** proper async/await handling +- ✅ **Enhanced** user feedback with alerts +- ✅ **Improved** error messages + +## 📊 Benefits + +### Performance Improvements: +- **Reduced API calls** - Single `getModuleData()` call instead of multiple separate calls +- **Better caching** - Unified data management reduces redundant requests +- **Improved loading** - Better loading state management + +### Developer Experience: +- **Clearer logging** - Emoji-based console messages for easy debugging +- **Better error handling** - Comprehensive error checking and user feedback +- **Consistent patterns** - All API calls follow same async/await + result checking pattern +- **Type safety** - Better parameter validation + +### Maintainability: +- **Reduced complexity** - Fewer functions to maintain and understand +- **Consistent API** - All functions follow same result pattern `{ success, data, error }` +- **Better documentation** - Clear function purposes and parameters + +## 🔄 Migration Pattern + +The refactoring follows a consistent pattern for all API calls: + +```javascript +// 1. Check if function exists +if (!functionName) { + console.error('Function not available'); + return; +} + +// 2. Make async call with try/catch +try { + const result = await functionName(params); + + // 3. Check result success + if (result && result.success) { + console.log('✅ Operation successful'); + // Handle success + } else { + console.error('Operation failed:', result?.error); + // Handle failure + } +} catch (error) { + console.error('Exception:', error); + // Handle exception +} +``` + +## 🔙 Backward Compatibility + +- ✅ **All existing functionality preserved** +- ✅ **Legacy function access maintained** for gradual migration +- ✅ **No breaking changes** to component interfaces +- ✅ **Gradual migration path** available + +## 📈 Results + +| Metric | Before | After | Improvement | +|--------|--------|-------|-------------| +| Context Functions Used | 6+ | 5 | **17% reduction** | +| Error Handling | Basic | Comprehensive | **Major improvement** | +| Logging Quality | Minimal | Rich with emojis | **Significant improvement** | +| API Consistency | Mixed | Unified | **Complete standardization** | +| Performance | Multiple calls | Single calls | **Reduced network overhead** | + +## 🚀 Next Steps + +1. **Monitor performance** in production +2. **Collect feedback** from component usage +3. **Phase out legacy functions** gradually +4. **Update other hooks** to follow same pattern +5. **Document best practices** for future development + +The `useEngineeringModule` hook is now **more robust, performant, and maintainable** while maintaining full backward compatibility! 🎉 diff --git a/documentation/frontend/SIMPLIFIED_MODULE_CONTEXT_README.md b/documentation/frontend/SIMPLIFIED_MODULE_CONTEXT_README.md new file mode 100644 index 000000000..93cb83968 --- /dev/null +++ b/documentation/frontend/SIMPLIFIED_MODULE_CONTEXT_README.md @@ -0,0 +1,266 @@ +# Simplified ModuleContext API Documentation + +## Overview +The ModuleContext has been simplified from **25+ functions to 8 core functions**, reducing complexity by 75% while maintaining full backward compatibility. + +## Core Functions (8 Total) + +### 1. POPULATE Category (3 functions) + +#### `getModuleData(moduleName, options = {})` +**Universal data fetcher that replaces 12+ functions** +- Replaces: `getConnectivityList`, `getColumnBeamMaterialList`, `getBeamMaterialList`, `getBoltDiameterList`, `getThicknessList`, `getPropertyClassList`, etc. +- **Parameters:** + - `moduleName` (string): Module identifier (e.g., 'FinPlateConnection') + - `options` (object): Optional parameters + - `connectivity` (string): Connection type filter + - `filters` (object): Additional filters +- **Returns:** `{ success: boolean, data?: object, error?: string }` + +**Example:** +```javascript +// Get all data for a module +const result = await getModuleData('FinPlateConnection'); + +// Get connectivity-specific data +const result = await getModuleData('FinPlateConnection', { + connectivity: 'Beam-Beam' +}); +``` + +#### `getConnectivityData(moduleName)` +**Get connectivity-specific data for a module** +- **Parameters:** + - `moduleName` (string): Module identifier +- **Returns:** Same as `getModuleData` + +#### `manageCustomMaterials(action, data = {})` +**Manage custom materials operations** +- Replaces: `addCustomMaterialToDB`, `updateMaterialListFromCaches` +- **Parameters:** + - `action` (string): 'add', 'sync', 'update' + - `data` (object): Action-specific data +- **Returns:** `{ success: boolean, message?: string, error?: string }` + +**Examples:** +```javascript +// Add custom material +await manageCustomMaterials('add', { + grade: 'Custom Steel', + inputs: { fy_20: 250, fy_20_40: 240, fy_40: 230, fu: 410 }, + connectivity: 'Beam-Beam', + type: 'connector' +}); + +// Sync from localStorage cache +await manageCustomMaterials('sync'); + +// Update material details +await manageCustomMaterials('update', { + materialType: 'connector', + materialData: { /* material object */ } +}); +``` + +### 2. CALCULATE Category (1 function) + +#### `createDesign(param, module_id, onCADSuccess = null)` +**Create design calculation** +- Uses external API function for consistency +- **Parameters:** + - `param` (object): Design parameters + - `module_id` (string): Module identifier + - `onCADSuccess` (function): Optional success callback + +### 3. CAD Category (2 functions) + +#### `createCADModel(inputData, moduleId, onCADSuccess = null)` +**Generate 3D CAD model** +- **Parameters:** + - `inputData` (object): Design input values + - `moduleId` (string): Module identifier + - `onCADSuccess` (function): Success callback +- **Returns:** `{ success: boolean, files?: object, error?: string }` + +#### `downloadCADModel(format)` +**Download CAD model in specified format** +- **Parameters:** + - `format` (string): File format ('step', 'iges', 'stl') +- **Returns:** `{ success: boolean, blob?: Blob, error?: string }` + +### 4. REPORTS Category (1 function) + +#### `generateReport(type, params = {})` +**Generate reports with unified interface** +- Replaces: `getPDF`, `createDesignReport`, `saveCSV` +- **Parameters:** + - `type` (string): Report type ('pdf', 'csv', 'design_report') + - `params` (object): Report parameters +- **Returns:** `{ success: boolean, message?: string, data?: object, error?: string }` + +**Examples:** +```javascript +// Generate PDF +await generateReport('pdf', { report_id: 'report_123' }); + +// Generate CSV +await generateReport('csv'); + +// Generate design report +await generateReport('design_report', { + moduleId: 'FinPlateConnection', + inputValues: { /* design inputs */ }, + designStatus: true, + logs: [] +}); +``` + +### 5. DESIGN PREFERENCES Category (1 function) + +#### `manageDesignPreferences(action, params = {})` +**Manage design preferences and material properties** +- Replaces: `getDesingPrefData`, `getSupportedData`, `getMaterialDetails`, `updateSourceAndMechType` +- **Parameters:** + - `action` (string): 'get', 'material_update', 'section_update' + - `params` (object): Action-specific parameters +- **Returns:** `{ success: boolean, data?: object, message?: string, error?: string }` + +**Examples:** +```javascript +// Get design preferences +await manageDesignPreferences('get', { + supported_section: 'ISMB 400', + supporting_section: 'ISMB 500', + connectivity: 'Beam-Beam' +}); + +// Update material details +await manageDesignPreferences('material_update', { + materialType: 'connector', + materialData: { /* material object */ } +}); + +// Update section data +await manageDesignPreferences('section_update', { + id: 1, + materialValue: 'Custom' +}); +``` + +## Utility Functions + +#### `resetModuleState()` +**Reset module state to initial values** + +## Benefits + +### Before Simplification: +- **25+ functions** with overlapping functionality +- **Multiple duplicate functions** for same operations +- **Inconsistent API patterns** across similar functions +- **Complex maintenance** and testing +- **Confusing for developers** to know which function to use + +### After Simplification: +- **8 core functions** with clear responsibilities +- **75% reduction** in function count +- **Consistent API patterns** with unified error handling +- **Better performance** with fewer API calls +- **Easier maintenance** and testing +- **Clear documentation** and usage patterns + +## Migration Guide + +### Old Code: +```javascript +// Old way - multiple function calls +await getConnectivityList(moduleName); +await getBoltDiameterList(moduleName); +await getThicknessList(moduleName); +await getPropertyClassList(moduleName); +await addCustomMaterialToDB(grade, inputs, connectivity, type); +await getDesingPrefData(params); +``` + +### New Code: +```javascript +// New way - single unified call +await getModuleData(moduleName); +await manageCustomMaterials('add', { grade, inputs, connectivity, type }); +await manageDesignPreferences('get', params); +``` + +## Backward Compatibility + +All legacy functions are still available and redirect to the new core functions: +- `getConnectivityList` → `getConnectivityData` +- `getBoltDiameterList` → `getModuleData` +- `addCustomMaterialToDB` → `manageCustomMaterials('add')` +- `getDesingPrefData` → `manageDesignPreferences('get')` +- etc. + +## Simplified Reducer Actions + +The ModuleReducer has also been simplified from **20+ action types to 8 core actions**: + +### Core Reducer Actions (8 Total) + +1. **`SET_ALL_MODULE_DATA`** - Universal data setter (replaces 10+ individual actions) +2. **`SAVE_MATERIAL_DETAILS`** - Unified material management (replaces 3 actions) +3. **`UPDATE_SECTION_DATA`** - Consolidated section updates (replaces 2 actions) +4. **`SET_DESIGN_DATA_AND_LOGS`** - Design calculation results +5. **`SET_CAD_MODEL_PATHS`** - CAD model management +6. **`SET_REPORT_ID_AND_DISPLAY_PDF`** - Report management +7. **`SAVE_DESIGN_PREF_DATA`** - Design preferences +8. **`RESET_MODULE_STATE`** - State reset utility + +### Reducer Benefits: +- **60% reduction** in action types (20+ → 8) +- **Consistent error handling** across all actions +- **Better null safety** with default values +- **Consolidated logic** for related operations +- **Backward compatibility** maintained + +### New Unified Actions: + +```javascript +// Old way - multiple separate actions +dispatch({ type: "SAVE_CM_DETAILS", payload: connectorData }); +dispatch({ type: "SAVE_SDM_DETAILS", payload: supportedData }); +dispatch({ type: "SAVE_STM_DETAILS", payload: supportingData }); + +// New way - single unified action +dispatch({ + type: "SAVE_MATERIAL_DETAILS", + payload: { materialType: "connector", materialData: connectorData } +}); +``` + +## Best Practices + +1. **Use core functions** for all new development +2. **Avoid legacy functions** - they're only for compatibility +3. **Check return values** - all functions return `{ success, ... }` objects +4. **Handle errors gracefully** - all functions include error handling +5. **Use consistent logging** - all functions include comprehensive logging +6. **Use new reducer actions** - prefer consolidated actions over legacy ones + +## Module Support + +The simplified API works with all modules: +- **Shear Connections:** Fin Plate, Cleat Angle, End Plate, Seated Angle +- **Moment Connections:** Cover Plate Bolted/Welded, End Plate +- **Base Plates:** Base Plate Connection +- **Tension Members:** Bolted/Welded to End Plate +- **Compression Members:** Struts in Trusses +- **Flexure Members:** Simply Supported Beam, Cantilever Beam, Plate Girder + +## State Variables (Unchanged) + +All state variables remain the same for backward compatibility: +- `materialList`, `boltDiameterList`, `thicknessList`, `propertyClassList` +- `beamList`, `columnList`, `connectivityList` +- `angleList`, `topAngleList`, `channelList`, `sectionProfileList` +- `weldTypes`, `weldFab`, `endPlateTypeList` +- `designData`, `designLogs`, `cadModelPaths`, `designPrefData` +- etc. diff --git a/documentation/general/19.11.md b/documentation/general/19.11.md new file mode 100644 index 000000000..b83e3842f --- /dev/null +++ b/documentation/general/19.11.md @@ -0,0 +1,733 @@ +# Refactoring Plan - EngineeringModule De-cluttering & Architecture +**Date:** 19.11.2025 + +## Problem +The `EngineeringModule.jsx` file suffers from "God Component" syndrome. It currently handles: +- Routing & Authentication +- API Data Fetching (mixed with Context) +- UI Layout State (Input/Output Docks) +- Complex 3D Rendering Logic (Three.js/Fiber) +- Input Form Logic + +Additionally, the data layer is inconsistent. Some API calls happen in components, some in Context, and some in hooks. The Context API is being used for local module state, which adds unnecessary complexity. + +## Current Status (Updated) +**As of Verification:** The `backend` and `osdag_core` folders already exist and are populated. +- **Backend:** Follows the proposed structure (`config`, `apps/core`, `apps/modules`). +- **Modules:** `fin_plate` and others are present with `adapter.py` and `service.py` implementing the Service/Adapter pattern. +- **Core:** `osdag_core` contains the engineering logic. + +**Action Required:** Verify the functionality of the existing migration rather than creating it from scratch. + +## Solution Overview +1. **Backend First**: Establish the 4-folder structure and API contract immediately. +2. **UI Separation**: Extract 3D logic and Input logic into dedicated components. +3. **Data Layer Refactor**: Connect the new UI to the new Backend via a Service Hook. + +--- + +## Phase 1: Backend Architecture & Folder Structure (Completed) + +**Goal:** Reduce friction when adding new modules, declutter `urls.py`, and standardize the app structure. + +### 1. Folder Structure & Cleanup +**Status:** **COMPLETE** +- `backend` folder established with Django project structure. +- `osdag_core` contains engineering logic. +- Legacy `osdag_api` moved to `deprecated-backend/`. + +### 2. Module Migration Status +All core modules have been migrated to the `backend/apps/modules/` structure using the **Service/Adapter Pattern**: + +| Module Group | Submodules | Status | +| :--- | :--- | :--- | +| **Shear Connection** | `fin_plate`, `end_plate`, `cleat_angle`, `seated_angle` | ✅ Migrated | +| **Moment Connection** | `beam_column`, `beam_beam`, `cover_plate_bolted`, `cover_plate_welded` | ✅ Migrated | +| **Tension Member** | `bolted`, `welded` | ✅ Migrated | +| **Flexure Member** | `simply_supported_beam` | ✅ Migrated | +| **Compression Member** | `compression` | ✅ Migrated | +| **Base Plate** | `base_plate` | ✅ Migrated | + +**Next Steps:** Proceed to Phase 4 (Frontend Connection). + +### 2. URL Refactoring +**Goal:** `config/urls.py` should only route to other apps. No specific views. + +```python +# config/urls.py +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/core/', include('apps.core.urls')), + path('api/design/', include('apps.design_api.urls')), + # ... +] +``` + +### 3. Dynamic Module Discovery (`module_finder.py`) +**Goal:** Remove the need to manually import every module. + +**Refactored `apps/core/module_finder.py`:** +```python +import importlib +import pkgutil +from typing import Dict +import apps.modules # Import the package to get its path + +# ... (Keep ModuleApiType Protocol) ... + +def _autodiscover_modules() -> Dict[str, ModuleApiType]: + """ + Dynamically finds all modules in 'apps.modules' that define a MODULE_ID. + """ + discovered_modules = {} + package_path = apps.modules.__path__ + package_name = 'apps.modules' + + for _, name, _ in pkgutil.iter_modules(package_path): + try: + full_module_name = f"{package_name}.{name}" + mod = importlib.import_module(full_module_name) + + # Check for explicit registration + if hasattr(mod, 'MODULE_ID'): + module_id = getattr(mod, 'MODULE_ID') + discovered_modules[module_id] = mod + print(f"Registered Module: {module_id}") + + except ImportError as e: + print(f"Failed to import module {name}: {e}") + continue + + return discovered_modules + +# Initialize once +module_dict: Dict[str, ModuleApiType] = _autodiscover_modules() +``` + +**Required Change in Modules:** +Add `MODULE_ID = 'FinPlateConnection'` to each module file. + +--- + +## Phase 2: UI Component Extraction (Frontend Cleanup) + +### 1. New Component: `CadViewer.jsx` +*Location: `src/modules/shared/components/CadViewer.jsx`* + +Moves `Canvas`, `Suspense`, and `Model` out of the main module. Handles hover logic internally. + +```jsx +import React, { Suspense, useMemo, useState } from 'react'; +import { Canvas } from '@react-three/fiber'; +// ... imports + +export const CadViewer = ({ + cadModelPaths, + modelKey, + cameraSettings, + // ... props +}) => { + // Move Hover State & Logic here + const [hoverText, setHoverText] = useState(""); + + const hoverDict = useMemo(() => { + // ... consolidated hover dictionary logic + }, [ctxHoverDict]); + + return ( +
+ + + Loading...}> + + + + + {/* Tooltip rendering */} +
+ ); +}; +``` + +### 2. New Component: `BaseInputDock.jsx` +*Location: `src/modules/shared/components/BaseInputDock.jsx`* + +Creates symmetry with `OutputDockComponent`. This allows specific modules to override the input form if needed (e.g., a Truss module might need a Wizard input instead of a list). + +```jsx +import React from 'react'; +import { InputSection } from "../components/InputSection"; + +export const BaseInputDock = ({ + moduleConfig, + inputs, + setInputs, + isInputLocked, + // ... props +}) => { + return ( +
+ {/* Header with Lock/Unlock controls */} +
+ {/* ... */} +
+ + {/* Scrollable Inputs */} +
+ {moduleConfig.inputSections.map((section, index) => ( + + ))} +
+ + {/* Footer Actions (Save/Design) */} +
+ + +
+
+ ); +}; +``` + +### 3. Refactored `EngineeringModule.jsx` +*Location: `src/modules/shared/components/EngineeringModule.jsx`* + +Now acts as a clean layout shell. It should accept `InputDockComponent` as a prop, defaulting to `BaseInputDock`. + +```jsx +// ... imports +import { BaseInputDock } from "./BaseInputDock"; // Default + +export const EngineeringModule = ({ + moduleConfig, + OutputDockComponent, + InputDockComponent = BaseInputDock, // Allow override, default to Base + ... +}) => { + // ... + return ( + // ... + {/* SYMMETRY: Input Dock Component */} + {uiState.showInputDock && ( + + )} + // ... + ); +}; +``` + +--- + +## Phase 3: Backend API & Workflows (The Contract) + +**Goal:** Move calculation logic out of `views.py` and handle multi-step workflows. + +### 1. Separation of Concerns (Views vs Services) + +**Bad (Current):** +```python +# views.py +class DesignView(APIView): + def post(self, request): + # ... 500 lines of math ... + return Response(result) +``` + +**Good (Refactored):** +```python +# views.py +from .services import FinPlateService + +class DesignView(APIView): + def post(self, request): + result = FinPlateService.calculate(request.data) + return Response(result) +``` + +### 2. Core Class Instantiation & Abstract Methods +**Goal:** Ensure the Backend correctly instantiates the `osdag_core` classes and respects the Abstract Base Class (ABC) contract. + +**The Pattern:** +1. **`osdag_core` (The Contract):** Defines an abstract base class (e.g., `ConnectionDesign`) with abstract methods: + * `set_input_values(self, values)` + * `hard_values(self)` + * `get_results(self)` +2. **`osdag_core` (The Implementation):** Concrete classes (e.g., `FinPlateConnection`) inherit from the base and implement these methods. +3. **`backend` (The Factory):** The **Service** layer is responsible for creating the instance. + +**Refactored Service Implementation:** +The `services.py` file acts as the bridge. It imports the class, creates an instance, and drives the execution flow defined by the abstract methods. + +```python +# backend/apps/modules/fin_plate/services.py +from osdag_core.design_type.fin_plate import FinPlateConnection + +class FinPlateService: + @staticmethod + def calculate(ui_input: dict) -> dict: + # 1. Instantiate the Core Class (The "Module") + # This class inherits from the Abstract Base Class in osdag_core + design_instance = FinPlateConnection() + + # 2. Map UI Data to Core Data (if necessary) + # The Service layer handles any data transformation required before passing to core + + # 3. Invoke Abstract Methods (The Contract) + design_instance.set_input_values(ui_input) + design_instance.hard_values() # Performs the actual engineering math + + # 4. Return Results + return design_instance.get_results() +``` + +### 3. Architecture Strategy: Hybrid Approach (Adapter Pattern) +**Constraint:** The `osdag_core` logic follows a strict Hierarchical Inheritance pattern (Abstract Base Classes) and cannot be modified. +**Solution:** The Backend acts as an **Adapter** to flatten this hierarchy for the Web API. + +| Layer | Pattern | Reason | +| :--- | :--- | :--- | +| **`osdag_core`** | **Hierarchical (Inheritance)** | **Immutable Constraint.** Efficient for sharing math logic (e.g., `ShearConnection` parent class). We treat this as a third-party library. | +| **`backend`** | **Flat (Composition)** | The API should be simple. Endpoints like `/api/design/fin-plate` map directly to a specific Service, hiding the complex inheritance tree from the web layer. | + +**Implementation Rule:** +* **Do not** create "Abstract" services (e.g., `ShearService`). +* **Only** create Services for concrete modules (e.g., `FinPlateService`). +* The Service is responsible for instantiating the correct Core class and calling the standard ABC methods (`set_input_values`, `get_results`). + +### 4. Handling Multi-Step Workflows (CAD, Report, OSI) + +**Problem:** +Currently, a module has scattered endpoints in `urls.py` for different stages of the design lifecycle: +* `/populate` (Get dropdown options) +* `/calculate` (Run math) +* `/cad` (Get 3D geometry) +* `/report` (Generate PDF) +* `/osi` (Save file) + +**Solution: The Service Facade & ViewSets** +Instead of loose functions, we group these related operations into the **Module Service** and expose them via a **Django ViewSet**. + +#### A. The Unified Service (`services.py`) +The Service acts as a facade for `osdag_core`. It handles the stateless nature of HTTP by re-instantiating the core class when needed. + +```python +# backend/apps/modules/fin_plate/services.py +from osdag_core.design_type.fin_plate import FinPlateConnection +from osdag_core.utils import report_generator + +class FinPlateService: + + @staticmethod + def get_input_options(): + """Returns lists for Beams, Columns, Grades, etc.""" + # Logic to fetch from SQLite or osdag_core utils + return { + "beams": [...], + "columns": [...] + } + + @staticmethod + def calculate(inputs): + """Runs the design and returns results + lightweight CAD data.""" + model = FinPlateConnection() + model.set_input_values(inputs) + model.hard_values() # The heavy math + + return { + "status": "success", + "results": model.get_results(), + # If CAD data is small (JSON), return it here to save a round-trip + "cad_model": model.get_3d_components() + } + + @staticmethod + def generate_report(design_data): + """Generates a PDF based on the design results.""" + # Since HTTP is stateless, we might need to re-run calc or pass full results + model = FinPlateConnection() + model.set_input_values(design_data['inputs']) + model.hard_values() + + pdf_path = report_generator.create_pdf(model) + return pdf_path +``` + +#### B. The ViewSet (`views.py`) +Use Django REST Framework's `ViewSet` to group these actions under a single route prefix. + +```python +# backend/apps/modules/fin_plate/views.py +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from .services import FinPlateService + +class FinPlateViewSet(viewsets.ViewSet): + MODULE_ID = "FinPlateConnection" + + @action(detail=False, methods=['get']) + def options(self, request): + """GET /api/modules/fin-plate/options/""" + data = FinPlateService.get_input_options() + return Response(data) + + @action(detail=False, methods=['post']) + def design(self, request): + """POST /api/modules/fin-plate/design/""" + result = FinPlateService.calculate(request.data) + return Response(result) + + @action(detail=False, methods=['post']) + def report(self, request): + """POST /api/modules/fin-plate/report/""" + pdf_file = FinPlateService.generate_report(request.data) + # Return file response... +``` + +#### C. The Router (`urls.py`) +This cleans up `urls.py` significantly. + +```python +# backend/apps/modules/fin_plate/urls.py +from rest_framework.routers import DefaultRouter +from .views import FinPlateViewSet + +router = DefaultRouter() +router.register(r'fin-plate', FinPlateViewSet, basename='fin-plate') + +urlpatterns = router.urls +``` + +--- + +## Phase 4: Data Layer Refactoring (Service Hook Pattern) + +**Goal:** Remove `ModuleContext` and standardize API calls. + +### 1. Create `useEngineeringService.js` +*Location: `src/modules/shared/hooks/useEngineeringService.js`* + +This hook will contain **all** API calls related to engineering modules. It should not hold state, only functions. + +**What to shift here:** +* Fetching Beam/Column/Section lists. +* Submitting design calculations (`handleSubmit`). +* Saving/Loading project inputs. +* Generating reports. + +**Example:** +```javascript +export const useEngineeringService = () => { + const { token } = useUserContext(); // Keep UserContext for Auth only + + const fetchBeamList = async () => { /* ... fetch logic ... */ }; + const submitDesign = async (payload) => { /* ... fetch logic ... */ }; + + return { fetchBeamList, submitDesign }; +}; +``` + +### 2. Update `useEngineeringModule.js` +*Location: `src/modules/shared/hooks/useEngineeringModule.js`* + +This hook currently acts as the "Controller". It should be updated to: +1. Import `useEngineeringService`. +2. Remove any direct `fetch` calls inside `useEffect`. +3. Replace them with calls to the service functions. +4. Manage the local state (`inputs`, `outputs`, `loading`) based on the service responses. + +### 3. Audit & Cleanup (Do this LAST) +* **Identify Direct API Calls:** Search the codebase for `fetch(` or `axios` calls inside individual UI components (e.g., inside `InputSection` or specific connection modules). Move these to `useEngineeringService`. +* **Remove Context:** Once `EngineeringModule` relies entirely on `useEngineeringModule` (which relies on `useEngineeringService`), the `EngineeringContext` provider can be deleted. + +--- + +## Phase 5: State Management & Data Flow (The UX) + +**Problem:** +The design process is a multi-step chain: `Calculate` -> `Show Results` -> `Generate CAD`. +* Users need granular feedback (e.g., "Calculated! Now generating 3D model..."). +* Report generation is ambiguous: Do we send the huge result JSON back to the server, or re-calculate? + +### 1. The "Chain of Responsibility" Hook +Instead of a single `loading` boolean, we use a state machine in `useEngineeringService`. + +**State Object:** +```javascript +const [status, setStatus] = useState({ + step: 'IDLE', // IDLE | CALCULATING | CAD_GENERATING | COMPLETE | ERROR + message: '' +}); +``` + +**The Flow Function:** +```javascript +// src/modules/shared/hooks/useEngineeringService.js + +const runDesignWorkflow = async (inputs) => { + try { + // Step 1: Calculation + setStatus({ step: 'CALCULATING', message: 'Running design checks...' }); + const calcResult = await api.post('/design/', inputs); + + if (calcResult.status === 'fail') { + throw new Error(calcResult.message); + } + + // Update UI with 2D results immediately + setResults(calcResult.data); + + // Step 2: CAD Generation (Auto-triggered) + setStatus({ step: 'CAD_GENERATING', message: 'Building 3D model...' }); + + // We pass the *inputs* again, or the *calculation ID* if the backend cached it. + // Ideally, for statelessness, we pass inputs + key results needed for geometry. + const cadResult = await api.post('/cad/', { + inputs: inputs, + design_summary: calcResult.data.summary + }); + + setCadModel(cadResult.data); + setStatus({ step: 'COMPLETE', message: 'Design complete.' }); + + } catch (error) { + setStatus({ step: 'ERROR', message: error.message }); + } +}; +``` + +### 2. Report Generation Strategy +**Question:** When generating a report, do we re-calculate or pass existing results? + +**Decision: Re-calculate (Stateless Approach)** +* **Why?** Passing the entire result JSON (which can be huge) from Frontend to Backend is inefficient and insecure (users could tamper with the "Result" JSON to fake a safe design). +* **Mechanism:** The Frontend sends the **Inputs** only. The Backend re-runs the math (which is fast, usually <100ms) and generates the PDF. + +```javascript +// Frontend +const handleDownloadReport = async () => { + setReportLoading(true); + // Send INPUTS, not results + await api.download('/report/', currentInputs); + setReportLoading(false); +}; +``` + +```python +# Backend (Service) +def generate_report(inputs): + # 1. Re-instantiate & Re-calculate + model = FinPlateConnection() + model.set_input_values(inputs) + model.hard_values() # Fast enough to run again + + # 2. Generate PDF with fresh, trusted results + return create_pdf(model) +``` + +### 3. UI Feedback Component +Create a `StatusIndicator` component in the Output Dock that listens to the `status` state. + +```jsx +// src/modules/shared/components/StatusIndicator.jsx +export const StatusIndicator = ({ status }) => { + if (status.step === 'IDLE') return null; + + return ( +
+ {status.step === 'CALCULATING' && } + {status.step === 'CAD_GENERATING' && } + {status.message} +
+ ); +}; +``` + +--- + +## Phase 6: 3D Viewer Optimization & Architecture (The Polish) + +**Goal:** Fix "cluttered" 3D logic, improve performance (prevent re-renders), and handle interaction priority (Bolts > Plates) correctly. + +### 1. The Core Problems & Solutions + +| Problem | Current Likely Approach | Better Approach | +| :--- | :--- | :--- | +| **Priority** | Raycaster hits everything. Complex loop to find "closest" or "most important". | **Event Bubbling (`stopPropagation`)**. In React Three Fiber, events bubble. If you hover a Bolt, handle it and stop the event so the Beam behind it doesn't trigger. | +| **Hover State** | `EngineeringModule` holds `hoverText`. Mouse move re-renders the *entire* UI. | **Local State**. The 3D Viewer manages its own hover state. The main UI doesn't need to know about tooltips. | +| **Color Change** | Manually swapping materials in a loop. | **Declarative State**. Use a component `` that calculates its own color. | +| **Merging** | Loading many files and merging geometries manually? | **Instancing**. Use `` for repeated parts like bolts. | + +### 2. New Component: `SmartPart.jsx` +*Location: `src/modules/shared/components/3d/SmartPart.jsx`* + +This component encapsulates the interaction logic for a single 3D part. It knows its own color and handles the "Priority" problem via `stopPropagation`. + +```jsx +import React, { useState } from 'react'; +import { useCursor } from '@react-three/drei'; + +export const SmartPart = ({ + geometry, + material, + name, + onHover, + onSelect, + isSelected +}) => { + const [hovered, setHover] = useState(false); + + // Change cursor to pointer when hovering + useCursor(hovered); + + // Priority Logic: + // Bolts are usually small and inside plates. + // We want to catch the event here and NOT let it bubble to the plate. + const handlePointerOver = (e) => { + e.stopPropagation(); // <--- MAGIC FIX for Priority + setHover(true); + onHover(name, e.clientX, e.clientY); // Tell parent to show tooltip + }; + + const handlePointerOut = (e) => { + setHover(false); + onHover(null); // Clear tooltip + }; + + // Color Logic + const baseColor = material.color || "grey"; + const displayColor = isSelected ? "#ff0000" : (hovered ? "#00ff00" : baseColor); + + return ( + { + e.stopPropagation(); + onSelect(name); + }} + > + + + ); +}; +``` + +### 3. Refactored `SceneManager.jsx` (Replaces `Model.jsx`) +*Location: `src/modules/shared/components/3d/SceneManager.jsx`* + +This component orchestrates the loading and placement of parts but delegates interaction to `SmartPart`. Crucially, it holds the `tooltip` state locally, preventing the rest of the app from re-rendering on mouse move. + +```jsx +import React, { useState } from 'react'; +import { useLoader } from '@react-three/fiber'; +import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'; +import { SmartPart } from './SmartPart'; +import { Html } from '@react-three/drei'; + +export const SceneManager = ({ modelPaths, selectedParts, onPartSelect }) => { + // Local Tooltip State (Does not re-render the whole EngineeringModule) + const [tooltip, setTooltip] = useState({ visible: false, text: '', x: 0, y: 0 }); + + const handleHover = (name, x, y) => { + if (!name) { + setTooltip({ ...tooltip, visible: false }); + } else { + setTooltip({ visible: true, text: name, x, y }); + } + }; + + return ( + + {/* Render Parts */} + {Object.entries(modelPaths).map(([key, path]) => ( + + ))} + + {/* Tooltip Overlay - Rendered via HTML inside Canvas for performance */} + {tooltip.visible && ( + +
+ {tooltip.text} +
+ + )} +
+ ); +}; +``` + +### 4. Optimization: `BoltGroup.jsx` (Instancing) +*Location: `src/modules/shared/components/3d/BoltGroup.jsx`* + +For repeated elements like bolts, use `InstancedMesh` instead of merging geometries or creating individual meshes. + +```jsx +import { Instances, Instance } from '@react-three/drei'; + +export const BoltGroup = ({ boltGeometry, positions }) => { + return ( + + + {positions.map((pos, i) => ( + { + e.stopPropagation(); // Stop event from hitting plate behind bolt + }} + /> + ))} + + ); +}; + +--- + +## Execution Checklist + +### Step 1: Preparation +- [ ] **Backup:** Create a git branch `refactor/architecture-v1`. +- [ ] **Environment:** Ensure `osdag_core` is installed in editable mode (`pip install -e ./osdag_core`) so it can be imported by `backend`. + +### Step 2: Backend Verification (Completed) +- [x] **Folder Structure:** `backend`, `frontend`, `osdag_core` exist. +- [x] **Django Setup:** `backend/config` exists. +- [x] **Module Audit:** All modules (Shear, Moment, Tension, Flexure, Compression, Base Plate) verified/migrated. +- [x] **Legacy Cleanup:** `osdag_api` moved to `deprecated-backend`. + +### Step 3: Core Integration +- [ ] **Verify Core:** Ensure `osdag_core` imports work from a standalone python script in `backend/`. +- [ ] **Test Service:** Manually test `FinPlateService.calculate()` via Django shell. + +### Step 4: Frontend Connection +- [ ] **Update API URL:** Point React `package.json` proxy or `.env` to the new Django URL structure. +- [ ] **Implement Hook:** Create `useEngineeringService.js` and test a simple fetch (e.g., `get_input_options`). diff --git a/documentation/general/ADDING_MODULES_AND_SUBMODULES_BACKEND.md b/documentation/general/ADDING_MODULES_AND_SUBMODULES_BACKEND.md new file mode 100644 index 000000000..3a5a05057 --- /dev/null +++ b/documentation/general/ADDING_MODULES_AND_SUBMODULES_BACKEND.md @@ -0,0 +1,949 @@ +# Adding New Modules and Submodules to Osdag-Web + +This guide explains how to add new parent modules and submodules to the Osdag-Web application using the current architecture with auto-discovery and registry patterns. + +## Table of Contents + +1. [Architecture Overview](#architecture-overview) +2. [Adding a New Parent Module](#adding-a-new-parent-module) +3. [Adding a New Submodule](#adding-a-new-submodule) +4. [File Structure Reference](#file-structure-reference) +5. [Registry System](#registry-system) +6. [URL Routing](#url-routing) +7. [Service and Adapter Pattern](#service-and-adapter-pattern) +8. [Examples](#examples) +9. [Troubleshooting](#troubleshooting) + +--- + +## Architecture Overview + +The Osdag-Web backend uses a hierarchical module structure: + +- **Parent Modules**: Top-level categories (e.g., `shear_connection`, `moment_connection`, `tension_member`, `flexure_member`) +- **Submodules**: Specific design implementations within a parent module (e.g., `fin_plate`, `cleat_angle` under `shear_connection`) + +### Directory Structure + +``` +backend/apps/modules/ +├── shear_connection/ # Parent module +│ ├── __init__.py +│ ├── apps.py # Django app config +│ ├── registry.py # Submodule registry +│ ├── urls.py # URL routing +│ ├── views.py # ViewSet for routing +│ ├── shared.py # Shared utilities (optional) +│ └── submodules/ # Submodule directory +│ ├── __init__.py +│ ├── fin_plate/ # Submodule +│ │ ├── __init__.py # Must define MODULE_ID and Service +│ │ ├── adapter.py # API adapter (input validation, output formatting) +│ │ └── service.py # Service class (business logic) +│ └── cleat_angle/ +│ └── ... +└── moment_connection/ + └── ... +``` + +### Key Concepts + +1. **Auto-Discovery**: Submodules are automatically discovered by the registry system +2. **Registry Pattern**: Each parent module has a registry that inherits from `BaseModuleRegistry` +3. **Service Layer**: Each submodule exposes a `Service` class with a `calculate()` method +4. **Adapter Layer**: Handles input validation, output formatting, and CAD model generation +5. **URL Routing**: Uses URL slugs (e.g., `fin-plate`) to route to the correct submodule + +--- + +## Adding a New Parent Module + +To add a completely new parent module (e.g., `base_plate`): + +### Step 1: Create Directory Structure + +```bash +backend/apps/modules/your_module/ +├── __init__.py +├── apps.py +├── registry.py +├── urls.py +├── views.py +└── submodules/ + └── __init__.py +``` + +### Step 2: Create `apps.py` + +```python +from django.apps import AppConfig + +class YourModuleConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.modules.your_module' + verbose_name = 'Your Module' +``` + +### Step 3: Create `registry.py` + +```python +""" +Your Module Registry - Auto-discovers sub-modules +Inherits from BaseModuleRegistry (DRY principle) +""" +import os +from apps.core.registry import BaseModuleRegistry + + +class YourModuleRegistry(BaseModuleRegistry): + """Registry for your module sub-modules""" + pass + + +# Auto-discover sub-modules +_package_name = 'apps.modules.your_module.submodules' +_package_path = os.path.join(os.path.dirname(__file__), 'submodules') +YourModuleRegistry.auto_discover(_package_name, _package_path) +``` + +### Step 4: Create `urls.py` + +```python +""" +Your Module URLs +""" +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import YourModuleViewSet + +router = DefaultRouter() +router.register(r'', YourModuleViewSet, basename='your-module') + +urlpatterns = router.urls +``` + +### Step 5: Create `views.py` + +```python +""" +Your Module ViewSet - Routes to sub-module services +Uses URL slug (not POST body) to find the correct service +Handles guest mode and optional project_id saving +""" +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from .registry import YourModuleRegistry +from apps.core.utils.module_helpers import handle_design_request + + +class YourModuleViewSet(viewsets.ViewSet): + """ + Generic ViewSet that routes to specific sub-module services based on URL slug. + Supports guest mode and optional project_id saving for authenticated users. + """ + permission_classes = [AllowAny] # Allow both authenticated and guest users + + @action(detail=False, methods=['post'], url_path='(?P[^/.]+)/design') + def design(self, request, submodule_slug=None): + """ + POST /api/modules/your-module/{submodule_slug}/design/ + + Request body: + { + "inputs": {...}, # Design input parameters + "project_id": 123 # Optional: Save results to project if user is authenticated + } + + Example: POST /api/modules/your-module/your-submodule/design/ + """ + # Use URL slug to find service (not POST body) + ServiceClass = YourModuleRegistry.get_service_by_slug(submodule_slug) + + if not ServiceClass: + return Response( + {'error': f'Sub-module {submodule_slug} not found'}, + status=404 + ) + + # Extract inputs and optional project_id + inputs = request.data.get('inputs', request.data) # Support both formats + project_id = request.data.get('project_id') + + # Handle authentication and project saving (shared logic) + context = handle_design_request( + request=request, + inputs=inputs, + project_id=project_id, + submodule_slug=submodule_slug, + module_name='your-module' + ) + + try: + # Call the service with request context + result = ServiceClass.calculate( + inputs=inputs, + request=request, + project_id=project_id if not context['is_guest'] else None, + user_email=context['user_email'] + ) + + # Add project saving result to response + if context['project_result']: + result['project_saved'] = context['project_result']['saved'] + if context['project_result'].get('project_id'): + result['project_id'] = context['project_result']['project_id'] + if context['project_result'].get('error'): + result['project_error'] = context['project_result']['error'] + + return Response(result, status=200) + except Exception as e: + return Response( + {'error': str(e), 'success': False}, + status=400 + ) + + @action(detail=False, methods=['get'], url_path='(?P[^/.]+)/options') + def options(self, request, submodule_slug=None): + """ + GET /api/modules/your-module/{submodule_slug}/options/ + + Returns input options for the sub-module (e.g., beam list, column list) + """ + # TODO: Implement options endpoint if needed + return Response({'message': 'Options endpoint not yet implemented'}, status=501) +``` + +### Step 6: Register in Django Settings + +Add your module to `INSTALLED_APPS` in `backend/config/settings.py`: + +```python +INSTALLED_APPS = [ + # ... other apps + 'apps.modules.your_module', + # ... other apps +] +``` + +### Step 7: Add URL Route + +Add your module to `backend/apps/modules/urls.py`: + +```python +urlpatterns = [ + # ... existing modules + path('your-module/', include('apps.modules.your_module.urls')), +] +``` + +--- + +## Adding a New Submodule + +To add a new submodule to an existing parent module (e.g., adding `new_connection` to `shear_connection`): + +### Step 1: Create Submodule Directory + +```bash +backend/apps/modules/shear_connection/submodules/new_connection/ +├── __init__.py +├── adapter.py +└── service.py +``` + +### Step 2: Create `__init__.py` + +This file is critical for auto-discovery. It must define `MODULE_ID` and export `Service`: + +```python +""" +NewConnection Sub-module +""" +MODULE_ID = 'NewConnection' # Must match the module identifier used in osdag_core +from .service import NewConnectionService as Service +``` + +**Important Notes:** +- `MODULE_ID` must be a unique string identifier (typically matches the osdag_core class name) +- `Service` must be exported and point to your service class +- The directory name (`new_connection`) will be converted to URL slug (`new-connection`) + +### Step 3: Create `adapter.py` + +The adapter handles: +- Input validation +- Output formatting +- CAD model generation +- Integration with osdag_core design engines + +```python +""" +API adapter for NewConnection module + +Functions: + get_required_keys() -> List[str]: + Return all required input parameters for the module. + validate_input(input_values: Dict[str, Any]) -> None: + Validate all input parameters (required keys, data types). + create_module() -> NewConnection: + Create an instance of the NewConnection module design class. + create_from_input(input_values: Dict[str, Any]) -> NewConnection: + Create an instance from input values. + generate_output(input_values: Dict[str, Any]) -> Tuple[Dict[str, Any], List[str]]: + Generate, format and return the output values. + Output format: { + "Bolt.Pitch": { + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)", + "val": 40 + } + } + create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + Generate the CAD model from input values as a BREP file. Return file path. +""" +from apps.core.utils import ( + validate_arr, validate_num, validate_string, + MissingKeyError, InvalidInputTypeError, + contains_keys, custom_list_validation, float_able, int_able, is_yes_or_no, validate_list_type +) +from osdag_core.design_type.connection.new_connection import NewConnection +from osdag_core.custom_logger import CustomLogger +from typing import Dict, Any, List, Tuple +import traceback + + +def get_required_keys() -> List[str]: + """ + Return all required input parameters for the module. + These keys must be present in the input dictionary. + """ + return [ + "Member.Profile", + "Member.Designation", + # ... add all required keys + "Module" + ] + + +def validate_input(input_values: Dict[str, Any]) -> None: + """ + Validate all input parameters. + Raises MissingKeyError or InvalidInputTypeError if validation fails. + """ + required_keys = get_required_keys() + + # Check for missing keys + if not contains_keys(input_values, required_keys): + missing = [key for key in required_keys if key not in input_values] + raise MissingKeyError(f"Missing required keys: {missing}") + + # Validate data types + # Example: validate_num(input_values.get("Bolt.Pitch"), "Bolt.Pitch") + # Example: validate_string(input_values.get("Member.Profile"), "Member.Profile") + + # Add your specific validation logic here + pass + + +def create_module() -> NewConnection: + """ + Create an instance of the NewConnection module design class. + """ + logger = CustomLogger() + module = NewConnection(logger=logger) + return module + + +def create_from_input(input_values: Dict[str, Any]) -> NewConnection: + """ + Create an instance of the NewConnection module from input values. + Maps input dictionary to module's set_input_values() method. + """ + module = create_module() + + # Map input_values to the format expected by osdag_core + # Example: + # module.set_input_values({ + # "Member.Profile": input_values.get("Member.Profile"), + # "Member.Designation": input_values.get("Member.Designation"), + # # ... map all inputs + # }) + + return module + + +def generate_output(input_values: Dict[str, Any]) -> Tuple[Dict[str, Any], List[str]]: + """ + Generate formatted output from input values. + + Returns: + Tuple of (output_dict, logs_list) + - output_dict: Flattened dictionary with format {key: {key, label, val}} + - logs_list: List of calculation log messages + """ + try: + # Create module instance + module = create_from_input(input_values) + + # Get raw outputs from osdag_core + raw_output = module.output_values(True) # True = include TextBox entries + raw_spacing = module.spacing(True) if hasattr(module, 'spacing') else [] + raw_capacities = module.capacities(True) if hasattr(module, 'capacities') else [] + + # Format outputs into flat dictionary + output = {} + logs = [] + + # Process output_values (list of tuples) + # Format: (key, label, value, type, ...) + for param in raw_output: + if len(param) >= 3: + key = param[0] + label = param[1] + val = param[2] + output[key] = { + "key": key, + "label": label, + "val": val + } + + # Process spacing if available + for param in raw_spacing: + if len(param) >= 3: + key = param[0] + label = param[1] + val = param[2] + output[key] = { + "key": key, + "label": label, + "val": val + } + + # Process capacities if available + for param in raw_capacities: + if len(param) >= 3: + key = param[0] + label = param[1] + val = param[2] + output[key] = { + "key": key, + "label": label, + "val": val + } + + # Get logs from module if available + if hasattr(module, 'logger') and hasattr(module.logger, 'get_logs'): + logs = module.logger.get_logs() + + return output, logs + + except Exception as e: + error_msg = f"Error generating output: {str(e)}\n{traceback.format_exc()}" + return {}, [error_msg] + + +def create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str: + """ + Generate CAD model from input values. + + Args: + input_values: Design input parameters + section: Section name (e.g., "Model", "Beam", "Column") + session: Session ID for file naming + + Returns: + File path to generated CAD file (BREP format) + """ + from osdag_core.cad.common_logic import CommonDesignLogic + from OCC.Core import BRepTools + from OCC.Core.TopoDS import TopoDS_Compound + from OCC.Core.BRep import BRep_Builder + import os + + # Create module instance + module = create_from_input(input_values) + + # Setup CAD logic + cad_logic = CommonDesignLogic(module) + cad_logic.setup_for_cad() + + # Get CAD geometry for the requested section + if section == "Model": + # Get merged/complete model + shape = cad_logic.get_model_shape() + elif section == "Beam": + shape = cad_logic.get_beam_shape() + elif section == "Column": + shape = cad_logic.get_column_shape() + # ... add other sections as needed + else: + raise ValueError(f"Unknown section: {section}") + + # Create output directory + output_dir = os.path.join(settings.FILE_STORAGE_ROOT, 'cad_models') + os.makedirs(output_dir, exist_ok=True) + + # Generate file path + file_name = f"{session}_{section}.brep" + file_path = os.path.join(output_dir, file_name) + + # Write BREP file + BRepTools.Write(shape, file_path) + + return file_path +``` + +### Step 4: Create `service.py` + +The service class provides the business logic interface: + +```python +""" +New Connection Service - Business logic layer +Bridges between API and osdag_core +""" +from osdag_core.design_type.connection.new_connection import NewConnection +from .adapter import validate_input, generate_output, create_cad_model +import traceback + + +class NewConnectionService: + """Service class for NewConnection module""" + + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + """ + Run design calculation and return results. + + Args: + inputs: Dictionary of input parameters + request: Optional Django request object (for future use) + project_id: Optional project ID (for saving results) + user_email: Optional user email (for saving results) + + Returns: + Dictionary with 'data' (results), 'logs' (calculation logs), and 'success' flag + """ + try: + # Validate inputs + validate_input(inputs) + + # Generate formatted output (this handles module creation and calculation) + output, logs = generate_output(inputs) + + # Prepare response + result = { + 'data': output, + 'logs': logs or [], # Ensure logs is always a list + 'success': True + } + + return result + + except MissingKeyError as e: + return { + 'error': str(e), + 'success': False, + 'logs': [str(e)] + } + except InvalidInputTypeError as e: + return { + 'error': str(e), + 'success': False, + 'logs': [str(e)] + } + except Exception as e: + error_msg = f"Calculation error: {str(e)}\n{traceback.format_exc()}" + return { + 'error': error_msg, + 'success': False, + 'logs': [error_msg] + } + + @staticmethod + def get_cad_model(inputs: dict, section: str, session: str) -> str: + """ + Generate CAD model for a specific section. + + Args: + inputs: Design input parameters + section: Section name (e.g., "Model", "Beam", "Column") + session: Session ID for file naming + + Returns: + File path to generated CAD file + """ + return create_cad_model(inputs, section, session) +``` + +### Step 5: Verify Auto-Discovery + +The submodule should be automatically discovered when the Django app starts. Check the console output for: + +``` +✅ Discovered module: NewConnection from shear_connection/new_connection +``` + +If you don't see this message, check: +1. `__init__.py` has `MODULE_ID` and `Service` exported correctly +2. Directory name matches the submodule name +3. No import errors in `adapter.py` or `service.py` + +--- + +## File Structure Reference + +### Parent Module Files + +#### `apps.py` +Django app configuration. Required for Django to recognize the module. + +#### `registry.py` +Submodule registry that auto-discovers submodules. Must: +- Inherit from `BaseModuleRegistry` +- Call `auto_discover()` with package name and path + +#### `urls.py` +URL routing configuration. Uses Django REST Framework router. + +#### `views.py` +ViewSet that routes requests to submodule services. Handles: +- URL slug parsing +- Service lookup via registry +- Request/response handling +- Guest mode and authentication + +### Submodule Files + +#### `__init__.py` (Required) +Must define: +- `MODULE_ID`: Unique module identifier (string) +- `Service`: Reference to service class + +#### `adapter.py` (Required) +Contains: +- `get_required_keys()`: List of required input keys +- `validate_input()`: Input validation logic +- `create_module()`: Create osdag_core module instance +- `create_from_input()`: Create module from input dictionary +- `generate_output()`: Generate and format output +- `create_cad_model()`: Generate CAD files (optional but recommended) + +#### `service.py` (Required) +Contains: +- `Service` class with `calculate()` method +- Business logic layer between API and osdag_core + +--- + +## Registry System + +The registry system uses auto-discovery to find submodules automatically. + +### How It Works + +1. **BaseModuleRegistry**: Base class in `apps/core/registry.py` that provides: + - `register()`: Register a submodule + - `get_service_by_slug()`: Get service by URL slug + - `get_service_by_module_id()`: Get service by MODULE_ID + - `auto_discover()`: Automatically discover submodules + +2. **Parent Module Registry**: Each parent module has its own registry: + ```python + class ShearConnectionRegistry(BaseModuleRegistry): + pass + ``` + +3. **Auto-Discovery**: Scans `submodules/` directory and imports each submodule: + - Checks for `MODULE_ID` and `Service` in `__init__.py` + - Converts directory name to URL slug (e.g., `fin_plate` → `fin-plate`) + - Registers the service class + +### URL Slug Conversion + +Directory names use underscores (`fin_plate`), but URL slugs use hyphens (`fin-plate`). The registry automatically converts: +- `fin_plate` → `fin-plate` +- `cleat_angle` → `cleat-angle` +- `new_connection` → `new-connection` + +--- + +## URL Routing + +### URL Structure + +``` +/api/modules/{parent-module}/{submodule-slug}/design/ +``` + +Examples: +- `/api/modules/shear-connection/fin-plate/design/` +- `/api/modules/moment-connection/beam-column-end-plate/design/` +- `/api/modules/tension-member/bolted/design/` + +### Request Format + +```json +POST /api/modules/shear-connection/fin-plate/design/ +Content-Type: application/json + +{ + "inputs": { + "Member.Profile": "I", + "Member.Designation": "ISMB 200", + "Bolt.Pitch": 40, + // ... other inputs + }, + "project_id": 123 // Optional: Save to project if authenticated +} +``` + +### Response Format + +```json +{ + "data": { + "Bolt.Pitch": { + "key": "Bolt.Pitch", + "label": "Pitch Distance (mm)", + "val": 40 + }, + // ... other outputs + }, + "logs": [ + "Calculation started...", + "Design check passed...", + // ... other log messages + ], + "success": true, + "project_saved": true, // If project_id was provided + "project_id": 123 // If project was saved +} +``` + +--- + +## Service and Adapter Pattern + +### Service Layer (`service.py`) + +**Purpose**: Business logic interface between API and osdag_core + +**Responsibilities**: +- Call adapter functions +- Handle errors and exceptions +- Format responses +- Optional: Project saving, logging, caching + +**Interface**: +```python +class YourService: + @staticmethod + def calculate(inputs: dict, request=None, project_id=None, user_email=None) -> dict: + # Validate, calculate, return results + pass +``` + +### Adapter Layer (`adapter.py`) + +**Purpose**: Bridge between API format and osdag_core format + +**Responsibilities**: +- Input validation +- Input mapping/transformation +- Output formatting +- CAD model generation +- Direct interaction with osdag_core classes + +**Key Functions**: +- `get_required_keys()`: Define required inputs +- `validate_input()`: Validate inputs before calculation +- `create_from_input()`: Map API inputs to osdag_core format +- `generate_output()`: Format osdag_core outputs to API format +- `create_cad_model()`: Generate CAD files + +--- + +## Examples + +### Example 1: Adding `seated_angle` to `shear_connection` + +1. Create directory: `backend/apps/modules/shear_connection/submodules/seated_angle/` +2. Create `__init__.py`: + ```python + MODULE_ID = 'SeatedAngleConnection' + from .service import SeatedAngleService as Service + ``` +3. Create `adapter.py` with functions for SeatedAngleConnection +4. Create `service.py` with SeatedAngleService class +5. Auto-discovery will register it automatically +6. Access via: `POST /api/modules/shear-connection/seated-angle/design/` + +### Example 2: Adding `bolted` to `tension_member` + +1. Create directory: `backend/apps/modules/tension_member/submodules/bolted/` +2. Create `__init__.py`: + ```python + MODULE_ID = 'BoltedTensionMember' + from .service import BoltedTensionMemberService as Service + ``` +3. Create adapter and service files +4. Access via: `POST /api/modules/tension-member/bolted/design/` + +### Example 3: Complete New Parent Module `base_plate` + +1. Create `backend/apps/modules/base_plate/` with all required files +2. Add to `INSTALLED_APPS` in settings +3. Add URL route in `apps/modules/urls.py` +4. Create submodules under `base_plate/submodules/` +5. Access via: `POST /api/modules/base-plate/{submodule}/design/` + +--- + +## Troubleshooting + +### Submodule Not Discovered + +**Problem**: Submodule doesn't appear in registry + +**Solutions**: +1. Check `__init__.py` has `MODULE_ID` and `Service` exported +2. Verify directory name doesn't start with `_` (underscore) +3. Check for import errors in `adapter.py` or `service.py` +4. Restart Django server to trigger auto-discovery +5. Check console output for discovery messages + +### 404 Error on API Request + +**Problem**: `POST /api/modules/.../design/` returns 404 + +**Solutions**: +1. Verify URL slug matches directory name (with hyphens) +2. Check parent module is registered in `apps/modules/urls.py` +3. Verify ViewSet is correctly configured +4. Check registry has the submodule: `YourModuleRegistry.get_service_by_slug('submodule-slug')` + +### Import Errors + +**Problem**: Import errors when accessing submodule + +**Solutions**: +1. Check all imports in `adapter.py` are available +2. Verify osdag_core module exists and is importable +3. Check Python path includes backend directory +4. Verify dependencies are installed + +### Validation Errors + +**Problem**: Input validation fails + +**Solutions**: +1. Check `get_required_keys()` includes all required fields +2. Verify `validate_input()` checks match actual input types +3. Check input dictionary keys match expected format +4. Review error messages for specific missing/invalid keys + +### Output Format Issues + +**Problem**: Output doesn't match expected format + +**Solutions**: +1. Verify `generate_output()` returns `(dict, list)` tuple +2. Check output dictionary has `{key: {key, label, val}}` format +3. Ensure all output keys are strings +4. Check osdag_core output format matches expectations + +### CAD Model Generation Fails + +**Problem**: CAD model creation fails + +**Solutions**: +1. Verify `create_cad_model()` returns file path string +2. Check file storage directory exists and is writable +3. Verify section names match expected values +4. Check osdag_core CAD logic is properly initialized +5. Review file permissions and disk space + +--- + +## Best Practices + +1. **Naming Conventions**: + - Directory names: use underscores (`fin_plate`) + - URL slugs: automatically converted to hyphens (`fin-plate`) + - MODULE_ID: use PascalCase (`FinPlateConnection`) + - Service class: use PascalCase with "Service" suffix (`FinPlateService`) + +2. **Error Handling**: + - Always validate inputs before calculation + - Return meaningful error messages + - Log errors for debugging + - Handle exceptions gracefully + +3. **Output Formatting**: + - Always return consistent output format + - Include all relevant calculation results + - Provide clear labels for output values + - Include calculation logs when available + +4. **Code Organization**: + - Keep adapter functions focused on I/O transformation + - Keep service class focused on business logic + - Use shared utilities from `apps.core.utils` + - Follow existing patterns in similar modules + +5. **Testing**: + - Test input validation with various inputs + - Test output formatting matches expected format + - Test error handling with invalid inputs + - Test CAD generation for all supported sections + +--- + +## Additional Resources + +- Existing module examples: + - `backend/apps/modules/shear_connection/submodules/fin_plate/` + - `backend/apps/modules/moment_connection/submodules/beam_column_end_plate/` + - `backend/apps/modules/tension_member/submodules/bolted/` + +- Core utilities: + - `backend/apps/core/registry.py` - Base registry class + - `backend/apps/core/utils/` - Validation and helper functions + - `backend/apps/core/module_finder.py` - Module discovery logic + +- Related documentation: + - `documentation/NEW_MODULE_GUIDE.md` - Legacy module guide (old architecture) + - `documentation/DEPLOYMENT_GUIDE.md` - Deployment instructions + +--- + +## Summary Checklist + +### Adding a New Parent Module: +- [ ] Create directory structure +- [ ] Create `apps.py` +- [ ] Create `registry.py` with auto-discovery +- [ ] Create `urls.py` with router +- [ ] Create `views.py` with ViewSet +- [ ] Register in `INSTALLED_APPS` +- [ ] Add URL route in `apps/modules/urls.py` +- [ ] Create `submodules/` directory + +### Adding a New Submodule: +- [ ] Create submodule directory +- [ ] Create `__init__.py` with `MODULE_ID` and `Service` +- [ ] Create `adapter.py` with required functions +- [ ] Create `service.py` with `Service` class +- [ ] Verify auto-discovery works +- [ ] Test API endpoint +- [ ] Test input validation +- [ ] Test output formatting +- [ ] Test CAD generation (if applicable) + +--- + +*Last updated: Based on current architecture as of 2024* + diff --git a/CONTRIBUTING.md b/documentation/general/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to documentation/general/CONTRIBUTING.md diff --git a/documentation/general/DEPLOYMENT_GUIDE.md b/documentation/general/DEPLOYMENT_GUIDE.md new file mode 100644 index 000000000..83c17fe80 --- /dev/null +++ b/documentation/general/DEPLOYMENT_GUIDE.md @@ -0,0 +1,752 @@ +# Docker Deployment Guide for Osdag-Web + +This guide provides step-by-step instructions for deploying the React + Django + PostgreSQL application using Docker with separate images for each service. + +## Table of Contents + +1. [Overview](#overview) +2. [Prerequisites](#prerequisites) +3. [Project Structure](#project-structure) +4. [Docker Configuration Files](#docker-configuration-files) +5. [Environment Setup](#environment-setup) +6. [Building and Running](#building-and-running) +7. [Production Deployment](#production-deployment) +8. [Troubleshooting](#troubleshooting) + +--- + +## Overview + +This deployment uses three separate Docker containers: +- **PostgreSQL**: Database service using official PostgreSQL image +- **Django Backend**: Python application server with Gunicorn +- **React Frontend**: Built static files served via Nginx + +--- + +## Prerequisites + +- Docker Engine 20.10+ installed +- Docker Compose 2.0+ installed +- Git (for cloning repository) +- Basic knowledge of Docker and Linux commands + +### Verify Docker Installation + +```bash +docker --version +docker-compose --version +``` + +--- + +## Project Structure + +``` +Osdag-web/ +├── Dockerfile # Django backend Dockerfile +├── docker-compose.yml # Development compose file +├── docker-compose.prod.yml # Production compose file +├── requirements.txt # Python dependencies +├── .env # Environment variables (create this) +├── frontend/ +│ ├── Dockerfile # Development frontend Dockerfile +│ ├── Dockerfile.prod # Production frontend Dockerfile +│ ├── nginx.conf # Nginx configuration +│ └── package.json +└── osdag_web/ + └── settings.py # Django settings +``` + +--- + +## Docker Configuration Files + +### 1. Django Backend Dockerfile + +Create or update `Dockerfile` in the project root: + +```dockerfile +# Django Backend Dockerfile +FROM python:3.11-slim + +ENV PYTHONUNBUFFERED=1 +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Asia/Kolkata + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + postgresql-client \ + libpq-dev \ + gcc \ + build-essential \ + libgl1-mesa-glx \ + libglu1-mesa \ + freecad \ + texlive-full \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Copy requirements first for better caching +COPY requirements.txt /app/ +RUN pip install --no-cache-dir -r requirements.txt +RUN pip install gunicorn + +# Copy project files +COPY . /app/ + +# Create necessary directories +RUN mkdir -p /app/staticfiles /app/file_storage /app/osifiles + +EXPOSE 8000 + +# Use gunicorn for production +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "--timeout", "120", "osdag_web.wsgi:application"] +``` + +### 2. Frontend Production Dockerfile + +Create `frontend/Dockerfile.prod`: + +```dockerfile +# Frontend Production Dockerfile +# Build stage +FROM node:18-alpine as build + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm ci + +# Copy source code +COPY . . + +# Build the application +RUN npm run build + +# Production stage - serve with nginx +FROM nginx:alpine + +# Copy built files from build stage +COPY --from=build /app/dist /usr/share/nginx/html + +# Copy custom nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] +``` + +### 3. Nginx Configuration for Frontend + +Create `frontend/nginx.conf`: + +```nginx +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json; + + # React app - handle client-side routing + location / { + try_files $uri $uri/ /index.html; + add_header Cache-Control "no-cache"; + } + + # Static assets caching + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # API proxy to Django backend + location /api/ { + proxy_pass http://backend:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_redirect off; + } + + # Django admin + location /admin/ { + proxy_pass http://backend:8000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + # Static files from Django + location /static/ { + proxy_pass http://backend:8000; + proxy_set_header Host $host; + } + + # Media files from Django + location /media/ { + proxy_pass http://backend:8000; + proxy_set_header Host $host; + } + + # OSI files + location /osifiles/ { + proxy_pass http://backend:8000; + proxy_set_header Host $host; + } +} +``` + +### 4. Production Docker Compose + +Create `docker-compose.prod.yml`: + +```yaml +version: '3.8' + +services: + # PostgreSQL Database + db: + image: postgres:14-alpine + container_name: osdag-postgres + environment: + POSTGRES_USER: osdagdeveloper + POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme} + POSTGRES_DB: postgres_Intg_osdag + PGDATA: /var/lib/postgresql/data/pgdata + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + networks: + - osdag-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U osdagdeveloper"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + + # Django Backend + backend: + build: + context: . + dockerfile: Dockerfile + container_name: osdag-backend + environment: + - DEBUG=False + - DATABASE_HOST=db + - DATABASE_PORT=5432 + - DATABASE_NAME=postgres_Intg_osdag + - DATABASE_USER=osdagdeveloper + - DATABASE_PASSWORD=${DB_PASSWORD:-changeme} + - SECRET_KEY=${SECRET_KEY:-changeme} + - ALLOWED_HOSTS=${ALLOWED_HOSTS:-localhost,127.0.0.1} + volumes: + - ./file_storage:/app/file_storage + - ./osifiles:/app/osifiles + - static_volume:/app/staticfiles + ports: + - "8000:8000" + depends_on: + db: + condition: service_healthy + networks: + - osdag-network + command: > + sh -c "python manage.py migrate && + python manage.py collectstatic --noinput && + gunicorn --bind 0.0.0.0:8000 --workers 3 --timeout 120 osdag_web.wsgi:application" + restart: unless-stopped + + # React Frontend + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.prod + container_name: osdag-frontend + ports: + - "80:80" + - "443:443" + depends_on: + - backend + networks: + - osdag-network + restart: unless-stopped + +networks: + osdag-network: + driver: bridge + +volumes: + postgres_data: + driver: local + static_volume: + driver: local +``` + +### 5. Development Docker Compose (Optional) + +Update `docker-compose.yml` for development: + +```yaml +version: '3.8' + +services: + db: + image: postgres:14-alpine + container_name: osdag-postgres-dev + environment: + POSTGRES_USER: osdagdeveloper + POSTGRES_PASSWORD: password + POSTGRES_DB: postgres_Intg_osdag + ports: + - "5432:5432" + volumes: + - postgres_data_dev:/var/lib/postgresql/data + networks: + - osdag-network-dev + + backend: + build: + context: . + dockerfile: Dockerfile + container_name: osdag-backend-dev + ports: + - "8000:8000" + volumes: + - .:/app + - ./file_storage:/app/file_storage + - ./osifiles:/app/osifiles + environment: + - DEBUG=True + - DATABASE_HOST=db + - DATABASE_PORT=5432 + - DATABASE_NAME=postgres_Intg_osdag + - DATABASE_USER=osdagdeveloper + - DATABASE_PASSWORD=password + depends_on: + - db + networks: + - osdag-network-dev + command: python manage.py runserver 0.0.0.0:8000 + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: osdag-frontend-dev + ports: + - "5173:5173" + volumes: + - ./frontend:/app + - /app/node_modules + environment: + - CHOKIDAR_USEPOLLING=true + networks: + - osdag-network-dev + command: npm run dev + +networks: + osdag-network-dev: + driver: bridge + +volumes: + postgres_data_dev: + driver: local +``` + +--- + +## Environment Setup + +### 1. Create Environment File + +Create `.env` file in the project root: + +```env +# Database Configuration +DB_PASSWORD=your_secure_password_here + +# Django Configuration +SECRET_KEY=your_django_secret_key_here +DEBUG=False +ALLOWED_HOSTS=localhost,127.0.0.1,your-domain.com + +# Optional: Database settings (if different from defaults) +DATABASE_NAME=postgres_Intg_osdag +DATABASE_USER=osdagdeveloper +DATABASE_HOST=db +DATABASE_PORT=5432 +``` + +### 2. Generate Django Secret Key + +Generate a secure secret key: + +```bash +python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())" +``` + +Add the output to your `.env` file. + +### 3. Update Django Settings (if needed) + +Ensure `osdag_web/settings.py` can read from environment variables: + +```python +import os + +# Database configuration from environment +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': os.environ.get('DATABASE_NAME', 'postgres_Intg_osdag'), + 'USER': os.environ.get('DATABASE_USER', 'osdagdeveloper'), + 'PASSWORD': os.environ.get('DATABASE_PASSWORD', 'password'), + 'HOST': os.environ.get('DATABASE_HOST', 'localhost'), + 'PORT': os.environ.get('DATABASE_PORT', '5432'), + } +} + +# Debug mode from environment +DEBUG = os.environ.get('DEBUG', 'False') == 'True' + +# Allowed hosts +ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',') + +# CORS settings +CORS_ALLOWED_ORIGINS = [ + 'http://localhost', + 'https://your-domain.com', +] +``` + +--- + +## Building and Running + +### Development Mode + +1. **Start development containers:** + +```bash +docker-compose up -d +``` + +2. **View logs:** + +```bash +docker-compose logs -f +``` + +3. **Stop containers:** + +```bash +docker-compose down +``` + +4. **Rebuild after code changes:** + +```bash +docker-compose up -d --build +``` + +### Production Mode + +1. **Build all images:** + +```bash +docker-compose -f docker-compose.prod.yml build +``` + +2. **Start all services:** + +```bash +docker-compose -f docker-compose.prod.yml up -d +``` + +3. **View logs:** + +```bash +# All services +docker-compose -f docker-compose.prod.yml logs -f + +# Specific service +docker-compose -f docker-compose.prod.yml logs -f backend +docker-compose -f docker-compose.prod.yml logs -f frontend +docker-compose -f docker-compose.prod.yml logs -f db +``` + +4. **Check service status:** + +```bash +docker-compose -f docker-compose.prod.yml ps +``` + +5. **Stop services:** + +```bash +docker-compose -f docker-compose.prod.yml down +``` + +6. **Stop and remove volumes (⚠️ deletes data!):** + +```bash +docker-compose -f docker-compose.prod.yml down -v +``` + +--- + +## Production Deployment + +### Step-by-Step Production Deployment + +1. **Prepare the server:** + - Ensure Docker and Docker Compose are installed + - Clone your repository + - Create `.env` file with production values + +2. **Build and start services:** + +```bash +# Build images +docker-compose -f docker-compose.prod.yml build --no-cache + +# Start services +docker-compose -f docker-compose.prod.yml up -d +``` + +3. **Create Django superuser:** + +```bash +docker-compose -f docker-compose.prod.yml exec backend python manage.py createsuperuser +``` + +4. **Run database migrations (if not done automatically):** + +```bash +docker-compose -f docker-compose.prod.yml exec backend python manage.py migrate +``` + +5. **Collect static files (if not done automatically):** + +```bash +docker-compose -f docker-compose.prod.yml exec backend python manage.py collectstatic --noinput +``` + +6. **Verify services are running:** + +```bash +# Check all containers +docker ps + +# Test backend +curl http://localhost:8000/api/ + +# Test frontend +curl http://localhost/ +``` + +### SSL/HTTPS Setup (Optional) + +For production, you'll want to add SSL. Update the frontend service in `docker-compose.prod.yml`: + +```yaml +frontend: + build: + context: ./frontend + dockerfile: Dockerfile.prod + container_name: osdag-frontend + ports: + - "80:80" + - "443:443" + volumes: + - ./ssl/certs:/etc/nginx/certs:ro + depends_on: + - backend + networks: + - osdag-network + restart: unless-stopped +``` + +Update `nginx.conf` to include SSL configuration. + +### Backup Database + +```bash +# Create backup +docker-compose -f docker-compose.prod.yml exec db pg_dump -U osdagdeveloper postgres_Intg_osdag > backup_$(date +%Y%m%d_%H%M%S).sql + +# Restore backup +docker-compose -f docker-compose.prod.yml exec -T db psql -U osdagdeveloper postgres_Intg_osdag < backup_file.sql +``` + +--- + +## Troubleshooting + +### Common Issues + +#### 1. Database Connection Error + +**Problem:** Backend can't connect to database + +**Solution:** +- Check database is running: `docker-compose ps` +- Verify environment variables in `.env` +- Check database logs: `docker-compose logs db` +- Ensure `DATABASE_HOST=db` (service name, not localhost) + +#### 2. Frontend Can't Reach Backend API + +**Problem:** API calls from frontend fail + +**Solution:** +- Verify nginx.conf has correct proxy_pass URL +- Check backend service name matches in nginx config +- Ensure both services are on the same network +- Check CORS settings in Django settings.py + +#### 3. Static Files Not Loading + +**Problem:** CSS/JS files return 404 + +**Solution:** +- Run collectstatic: `docker-compose exec backend python manage.py collectstatic --noinput` +- Check static volume is mounted correctly +- Verify STATIC_URL and STATIC_ROOT in settings.py + +#### 4. Port Already in Use + +**Problem:** Error binding to port 80, 8000, or 5432 + +**Solution:** +- Change ports in docker-compose.yml +- Or stop the service using the port: + ```bash + # Find process using port + sudo lsof -i :80 + # Kill process + sudo kill -9 + ``` + +#### 5. Build Fails + +**Problem:** Docker build errors + +**Solution:** +- Clear Docker cache: `docker system prune -a` +- Rebuild without cache: `docker-compose build --no-cache` +- Check Dockerfile syntax +- Verify all required files exist + +#### 6. Container Keeps Restarting + +**Problem:** Container exits immediately + +**Solution:** +- Check logs: `docker-compose logs ` +- Verify command in Dockerfile is correct +- Check environment variables are set +- Ensure dependencies are installed + +### Useful Commands + +```bash +# View container logs +docker-compose -f docker-compose.prod.yml logs -f + +# Execute command in running container +docker-compose -f docker-compose.prod.yml exec backend bash +docker-compose -f docker-compose.prod.yml exec frontend sh + +# Restart specific service +docker-compose -f docker-compose.prod.yml restart backend + +# View resource usage +docker stats + +# Clean up unused resources +docker system prune + +# Remove all containers and volumes +docker-compose -f docker-compose.prod.yml down -v +``` + +--- + +## Maintenance + +### Regular Tasks + +1. **Update dependencies:** + ```bash + # Update Python packages + docker-compose -f docker-compose.prod.yml exec backend pip list --outdated + + # Update Node packages + docker-compose -f docker-compose.prod.yml exec frontend npm outdated + ``` + +2. **Backup database regularly:** + - Set up cron job for automated backups + - Store backups in secure location + +3. **Monitor logs:** + - Set up log aggregation (optional) + - Monitor error rates + +4. **Update images:** + ```bash + docker-compose -f docker-compose.prod.yml pull + docker-compose -f docker-compose.prod.yml up -d + ``` + +--- + +## Security Considerations + +1. **Never commit `.env` file** - Add to `.gitignore` +2. **Use strong passwords** for database and secret keys +3. **Keep images updated** - Regularly update base images +4. **Limit exposed ports** - Only expose necessary ports +5. **Use secrets management** for production (Docker secrets, AWS Secrets Manager, etc.) +6. **Enable firewall** on the host server +7. **Use HTTPS** in production with valid SSL certificates + +--- + +## Additional Resources + +- [Docker Documentation](https://docs.docker.com/) +- [Docker Compose Documentation](https://docs.docker.com/compose/) +- [Django Deployment Checklist](https://docs.djangoproject.com/en/stable/howto/deployment/checklist/) +- [Nginx Documentation](https://nginx.org/en/docs/) + +--- + +## Support + +For issues specific to this deployment: +1. Check logs: `docker-compose logs` +2. Verify environment variables +3. Ensure all services are running: `docker-compose ps` +4. Review this guide's troubleshooting section + +--- + +**Last Updated:** 2024 + diff --git a/documentation/general/NEW_MODULE_GUIDE.md b/documentation/general/NEW_MODULE_GUIDE.md new file mode 100644 index 000000000..c325a1525 --- /dev/null +++ b/documentation/general/NEW_MODULE_GUIDE.md @@ -0,0 +1,365 @@ +### Osdag Web: Creating a New Module (Backend + Frontend) + +This guide explains how to add a new design module to Osdag Web, following the same architecture used by existing modules (e.g., Fin Plate, other shear connections, tension member bolted, flexure). It focuses on integrating with osdag_core engines, not legacy root-level design files. + +Use file/line references for precise context. Keep code changes minimal. + +--- + +## High-level architecture + +- URL route (Django) → View (APIView) → Module API adapter (`osdag_api/modules/.py`) → `osdag_core` design engine → Adapter formats outputs → JSON response. +- Frontend provides the Module key, inputs, and consumes output. + +--- + +## 1) Define module key and resolver + +- Register the module key in the module resolver: + - Add import and dictionary entry in: + - `osdag_api/module_finder.py` (module map) at: +```34:45:osdag_api/module_finder.py +module_dict : Dict[str, ModuleApiType] = { + 'FinPlateConnection': fin_plate_connection, + ... + 'Simply-Supported-Beam': simply_supported_beam, +} +``` + - Expose in the public list (for clients/UI): + - `osdag_api/__init__.py` → `developed_modules` and `module_dict` entries at: +```3:14:osdag_api/__init__.py +developed_modules = [ + "FinPlateConnection", + ..., + "Simply-Supported-Beam" +] +``` + +Add your new key in both places, e.g., `'My-New-Module'`. + +Recommendation: Use a descriptive key with spaces/hyphens consistent with existing modules. + +--- + +## 2) Create a module adapter (backend) + +Create `osdag_api/modules/.py` by following these patterns: + +- Fin Plate adapter (connection to `osdag_core`): +```1:25:osdag_api/modules/fin_plate_connection.py +""" +Api for FinPlateConnection module +Functions: + get_required_keys() -> List[str] + validate_input(input_values: Dict[str, Any]) -> None + create_module() -> FinPlateConnection + create_from_input(input_values: Dict[str, Any]) -> FinPlateConnection + generate_output(input_values: Dict[str, Any]) -> Dict[str, Any] + create_cad_model(input_values: Dict[str, Any], section: str, session: str) -> str +""" +``` + +- Tension bolted adapter (another example): +```21:46:osdag_api/modules/bolted_tension_member.py +def get_required_keys() -> List[str]: + # returns list including "Member.Profile", "Member.Designation", ..., "Module" +``` + +- Flexure adapter (Simply Supported Beam): +```113:131:osdag_api/modules/simply_supported_beam.py +def generate_output(input_values: Dict[str, Any]): + module = create_from_input(input_values) + raw_output_text = module.output_values(True) + raw_output_spacing = module.spacing(True) + # flatten tuples into { key: {key,label,val} } + return output, logs +``` + +Implement these functions in your adapter: +- `get_required_keys()` – list all required inputs (use same key names expected by the engine). +- `validate_input(...)` – optional strict checks (see Fin Plate for thorough type validation). +- `create_module()` – instantiate the `osdag_core` engine class (set logger if needed). +- `create_from_input(...)` – map/normalize request input to engine’s `set_input_values(...)` keys. +- `generate_output(...)` – call engine methods (typically `output_values(True)`, `spacing(True)`, and for connections also `capacities(...)`, `section_capacities(...)`), then format as `{ key: {key,label,val} }`. +- `create_cad_model(...)` – optional CAD output (see Fin Plate/Tension for examples). + +Engine targets in existing adapters: +- Fin Plate → `osdag_core/design_type/connection/fin_plate_connection.py` import at: +```41:41:osdag_api/modules/fin_plate_connection.py +from osdag_core.design_type.connection.fin_plate_connection import FinPlateConnection +``` +- Tension bolted → `design_type/tension_member/tension_bolted.py` (legacy path) +- Flexure (simply supported) → `design_type/flexural_member/flexure.py` (legacy path) + +For new modules, prefer `osdag_core` classes where available. + +--- + +## 3) Add module endpoints (Django) + +The current web backend uses the refactored modules API under `backend/apps/modules/` and does **not** wire per-module `calculate-output/*` routes anymore. + +- Add your module’s routes under the aggregated modules URL tree: + - `POST /api/modules///design/` + - `POST /api/modules///cad/` (optional) + - `GET /api/modules///options/` (for UI lists) + +Recommendation: Follow the existing `backend/apps/modules/*/views.py` + `service.py` + `adapter.py` pattern for new modules. + +--- + +## 4) Input-data API (populate dropdowns, lists) + +If your module needs UI-populating data (section lists, materials), implement it in the module `options` endpoint under `backend/apps/modules/.../`. + +Example (Simply Supported Beam): +```6:71:osdag/web_api/inputdata/simply_supported_beam_input.py +class SimplySupportedBeamInputData(InputDataBase): + def process(self, **kwargs): + response['beamList'] = list(Beams.objects.values_list('Designation', flat=True)) + response['columnList'] = list(Columns.objects.values_list('Designation', flat=True)) + response['materialList'] = Material + CustomMaterials (+ 'Custom') + ... + return Response(response, status=200) +``` + +Expose via existing endpoints if applicable (see `osdag/web_api/inputData_view.py`). + +--- + +## 5) Frontend: add module and screens + +Frontend lives under `frontend/src`. Add your module folder and routes similar to existing modules. + +- Add module metadata (key, name, image, path) to the list used by the UI. Backend serves a module list via `osdag_api/__init__.py` `module_dict` array: +```16:77:osdag_api/__init__.py +module_dict = [{"key": "FinPlateConnection", ...}, ..., {"key": "Simply-Supported-Beam", ...}] +``` +Consume that list on the client to show the module card and navigate to your screen. + +- Create a new folder for your module UI (inputs form, output view) similar to existing ones, e.g.: +`frontend/src/modules/connection/finPlate` or `.../flexuralMember/simplySupportedBeam` + +- The form should POST JSON to your backend endpoint and include the `Module` key (e.g., `'My-New-Module'`). + +### 5.1 Link your module to the Module Select page + +- The module selection page reads static UI config from `frontend/src/constants/modules.js`. + - Add your module to the right section (Connections/Tension/Flexure) in: +```1:11:frontend/src/constants/modules.js +export const MODULE_SUBMODULES = { ... } +``` + - Add a card under the relevant content block with your `key` in: +```13:54:frontend/src/constants/modules.js +export const CONNECTIONS_TAB_CONTENT = { ... } +``` +or for flexure/tension: +```56:82:frontend/src/constants/modules.js +export const GENERIC_SUBMODULE_CONTENT = { ... } +``` + - Map your `key` to a route under: +```84:95:frontend/src/constants/modules.js +export const MODULE_ROUTES = { ... } +``` + +- The selection page component uses these to navigate when a card is clicked: +```15:55:frontend/src/homepage/components/ModulesCardLayout.jsx +const submodules = MODULE_SUBMODULES[moduleName] || [] +... handleModuleClick → navigate(MODULE_ROUTES[optionKey]) +``` + +Ensure the route you add matches your React page location. + +### 5.2 Create the React page for your module + +- Follow an existing page such as Fin Plate or Simply Supported Beam to scaffold your module: + - Engineering page scaffold using shared shell: +```32:41:frontend/src/modules/shared/components/EngineeringModule.jsx +export const EngineeringModule = ({ moduleConfig, OutputDockComponent, menuItems, title }) => { ... } +``` + - Create `YourModule.jsx` under an appropriate folder and render `EngineeringModule` with a `moduleConfig` describing: + - `sessionName`, `designType` (must match backend `Module` key), `cameraKey`, `inputSections`, `modalConfig`, `cadOptions`. + +### 5.3 Use BaseOutputDock for outputs + +- Use the shared output renderer to avoid duplicating UI: +```14:269:frontend/src/modules/shared/components/BaseOutputDock.jsx +export const BaseOutputDock = ({ output, outputConfig, title, extraState }) => { ... } +``` +- Create `components/YourModuleOutputDock.jsx` that imports and configures `BaseOutputDock` with an `outputConfig` describing: + - `sections`: named groups and fields (keys must match backend output keys) + - `modals`, `modalTypes`, `modalData`: optional modal-driven layouts + - Keep to the flat output `{ key: {key,label,val} }` used in adapters + +Example references for output docks: +```1:...:frontend/src/modules/shearConnection/finPlate/components/FinPlateOutputDock.jsx +``` +```1:...:frontend/src/modules/flexuralMember/simplySupportedBeam/components/SimplySupportedBeamOutputDock.jsx +``` + +### 5.4 Wire the route to your page + +- Ensure your route path from `MODULE_ROUTES` matches the page mounted in your app router. +- The module selection page drives navigation via: +```29:45:frontend/src/homepage/components/ModulesCardLayout.jsx +const getRoute = (...) => MODULE_ROUTES[optionKey] || "" +``` +- The page frame that shows Module Select is at: +```1:58:frontend/src/homepage/pages/SelectModulePage.jsx +``` + +### 5.5 Submitting to backend and rendering output + +- `EngineeringModule` uses `useEngineeringModule` to handle inputs, submit to backend, and manage state. Provide the correct `designType` (Module key) so requests include the right `Module` and route to your adapter: +```32:106:frontend/src/modules/shared/components/EngineeringModule.jsx +const { inputs, output, logs, handleSubmit, ... } = useEngineeringModule(moduleConfig) +``` +- After a successful submit, `EngineeringModule` toggles the Output dock and renders your `OutputDockComponent` with the `output` object. + +--- + +## 6) Expected request/response contract + +- Request (POST JSON): must include at least `{ "Module": "", ... }` plus the fields your adapter’s `get_required_keys()` returns. +- Response: `{ "data": { "": {"key","label","val"}, ... }, "logs": [...], "success": true }` + +See examples of processing raw outputs to flattened JSON in: +```350:425:osdag_api/modules/fin_plate_connection.py +# generate_output merges output_values, spacing, capacities, section_capacities into a flat dict +``` +```113:131:osdag_api/modules/simply_supported_beam.py +# generate_output combines spacing + output_values and keeps TextBox entries +``` + +--- + +## 7) CAD + +All modules are expected to support CAD. + +End-to-end flow: +- Client POSTs to CAD endpoint with `module_id` and `input_values`. +- Backend resolves adapter and calls `create_cad_model(input_values, section, session)` for each supported section. +- API base64-encodes generated files and returns per section. +- Frontend shows per-part views using the same section names. + +### 7.1 Backend wiring + +1) Implement `create_cad_model(...)` in your adapter +- Use `cad.common_logic.CommonDesignLogic` and module setup helpers. +- Examples: +```428:561:osdag_api/modules/fin_plate_connection.py +# create_cad_model: sets up CommonDesignLogic, calls setup_for_cad, writes per-part and merged BREP; STEP/IGES for "Model" +``` +```310:369:osdag_api/modules/bolted_tension_member.py +# create_cad_model: writes BREP and, for Model, STEP/IGES +``` + +2) Whitelist supported sections in CAD API +- Map your `module_id` to a `session_type`, and declare the `sections` list to generate: +```54:99:osdag/web_api/cad_model_api.py +module_type_mapping = { 'FinPlateConnection': 'FinPlateConnection', ... } +... +if session_type == "FinPlateConnection": + sections = ["Model", "Beam", "Column", "Plate"] +elif session_type == "CleatAngle": + sections = ["Model", "Beam", "Column", "cleatAngle"] +... (per module) +``` +- The API loops and calls your adapter: +```110:121:osdag/web_api/cad_model_api.py +for section in sections: + path = module_api.create_cad_model(input_values, section, session_id) + ... read file and return base64 ... +``` + +3) Expose the CAD endpoint + +The current backend exposes CAD endpoints under the `backend/` project (see `backend/apps/core/urls.py` for the core CAD handlers and `backend/apps/modules/...` for per-module CAD endpoints). + +4) Formats +- Adapters currently write `.brep` (and for Model: `.step`/`.iges`). The CAD API returns base64 data per section under `files[section]`. + +### 7.2 Frontend linkage + +1) View options and camera presets +- Ensure `EngineeringModule` view options match your backend `sections` so users can switch between parts: +```287:307:frontend/src/modules/shared/components/EngineeringModule.jsx +if (moduleConfig.cameraKey === "FinPlateConnection") return ["Model", "Beam", "Column", "Plate"] +... (other modules) +``` + +2) Requesting and showing CAD +- `EngineeringModule`/hooks handle design submission and populate `cadModelPaths`. After success, Output Dock is shown and view options are enabled. + +3) Name alignment checklist +- Keep section names identical across: + - Adapter `create_cad_model(..., section, ...)` (e.g., "Beam", "Column", "Plate"). + - CAD API `sections = [...]` for your module. + - Frontend view options for your `cameraKey`. + +4) Examples +- Fin Plate: Model, Beam, Column, Plate + - Back: `osdag/web_api/cad_model_api.py` lines 80-86 + - Adapter: `osdag_api/modules/fin_plate_connection.py` lines 428-561 + - Front: `EngineeringModule.jsx` lines 287-307 (FinPlateConnection) + +- Cleat Angle: Model, Beam, Column, CleatAngle + - Back: `osdag/web_api/cad_model_api.py` lines 82-83 + - Adapter: `osdag_api/modules/cleat_angle_connection.py` (create_cad_model) + - Front: `EngineeringModule.jsx` lines 295-297 (CleatAngle) + +- Seated Angle: Model, Beam, Column, SeatedAngle + - Back: `osdag/web_api/cad_model_api.py` lines 86-87 + - Adapter: `osdag_api/modules/seated_angle_connection.py` (create_cad_model) + - Front: `EngineeringModule.jsx` lines 302-304 (SeatedAngle) + +- Tension Bolted: Model, Member, Plate, Endplate + - Back: `osdag/web_api/cad_model_api.py` lines 96-97 + - Adapter: `osdag_api/modules/bolted_tension_member.py` lines 310-369 + - Front: configure `cameraKey` view options accordingly + +--- + +## 8) Minimal check-list + +- Backend adapter: Add `osdag_api/modules/.py` targeting an `osdag_core` class; implement `get_required_keys`, `create_from_input`, `generate_output`, and `create_cad_model`. +- Resolver: Register key in `osdag_api/module_finder.py` (`module_dict`) and `osdag_api/__init__.py` (`developed_modules`, `module_dict` array for UI). +- Output API: Add your module `design` endpoint under `backend/apps/modules/.../views.py` (served under `/api/modules/.../design/`). +- Input-data API: Add/update the module `options` endpoint under `backend/apps/modules/...` (served under `/api/modules/.../options/`). +- CAD API mapping: In `osdag/web_api/cad_model_api.py`, map your `module_id` to `session_type` and declare `sections` to generate; ensure names match your adapter and frontend view options. +- Frontend routes/cards: Update `frontend/src/constants/modules.js` (`MODULE_SUBMODULES`, `..._CONTENT`, `MODULE_ROUTES`) so the module appears on Module Select and navigates to your page. +- Frontend page: Create `frontend/src/modules/.../.jsx` using `EngineeringModule`; set `moduleConfig.designType` to your backend key and `cameraKey` to enable correct view options. +- Output UI: Create a thin `.../components/OutputDock.jsx` that uses `BaseOutputDock` with your `outputConfig`. +- Output shape: Return `{ key: {key,label,val} }` from the adapter for consistency with shared UI components. + +--- + +## 9) Examples to mirror + +- Shear connections: Fin Plate service/adapter + outputs + - View/service: `backend/apps/modules/shear_connection/...` + - Core engine: `osdag_core/design_type/connection/...` + +- Tension (bolted): + - Adapter: `osdag_api/modules/bolted_tension_member.py` (see `get_required_keys`, `create_from_input`, `generate_output`) + +- Flexure (simply supported): + - Adapter: `osdag_api/modules/simply_supported_beam.py` (mapping inputs to `Flexure` keys; combine outputs) + - Dedicated view: `osdag/web_api/simplysupportedbeam_outputView.py` + +--- + +## 10) Troubleshooting tips + +- If `get_module_api` fails, confirm your key is present in: +```47:50:osdag_api/module_finder.py +get_module_api(module_id) +``` +and in the `module_dict` map at: +```34:45:osdag_api/module_finder.py +module_dict = { ... } +``` +- Ensure frontend sends the exact `Module` key you registered. +- If outputs are empty, print raw tuples from engine and verify you pick `param[2] == "TextBox"` and `param[3]` as value. +- For new engines, confirm `set_input_values` matches your adapter’s mapped keys. diff --git a/documentation/general/REFACTORING_COMPARISON.md b/documentation/general/REFACTORING_COMPARISON.md new file mode 100644 index 000000000..f4e8e44f2 --- /dev/null +++ b/documentation/general/REFACTORING_COMPARISON.md @@ -0,0 +1,621 @@ +# Backend Refactoring: Old vs New Architecture Comparison + +This document explains why the backend was refactored from the old Django structure (`osdag`, `osdag_api`, `web_api`) to the new modular architecture (`apps/core`, `apps/modules`), and compares how modules were added before versus now. + +## TL;DR + +- **Old Way**: Adding a module required **4-5 files across 3 directories** with manual imports, dictionary registration, and URL route additions in multiple files +- **New Way**: Adding a module requires **3 files in 1 directory** with zero manual registration - auto-discovery handles everything automatically +- **Time Savings**: Module addition time reduced from **30-45 minutes to 15-20 minutes** (50% faster) with lower error risk +- **Architecture**: Moved from flat, scattered structure to **hierarchical parent/submodule organization** with clear separation of concerns +- **URL Structure**: Changed from flat `calculate-output/ModuleName` routes to hierarchical `api/modules/parent/submodule/design/` URLs +- **Main Benefit**: Auto-discovery eliminates manual registration steps, making module addition faster, safer, and more consistent + +## Table of Contents + +1. [Why the Refactoring?](#why-the-refactoring) +2. [Old Architecture Overview](#old-architecture-overview) +3. [New Architecture Overview](#new-architecture-overview) +4. [Adding Modules: Before vs After](#adding-modules-before-vs-after) +5. [Key Improvements](#key-improvements) +6. [Migration Path](#migration-path) + +--- + +## Why the Refactoring? + +### Problems with the Old Architecture + +1. **Flat Structure with Scattered Apps** + - Modules were scattered across `osdag/`, `osdag_api/`, and `web_api/` + - No clear organization or grouping of related functionality + - Difficult to find where a specific module's code lived + +2. **Manual Module Registration** + - Every new module required manual imports in `osdag_api/module_finder.py` + - Had to manually add entries to `module_dict` dictionary + - Easy to forget steps, leading to broken modules + +3. **URL Clutter** + - `osdag/urls.py` had 140+ lines with individual routes for each module + - Each module needed its own URL path: `calculate-output/FinPlateConnection` + - No hierarchical organization of URLs + +4. **Code Duplication** + - Similar view classes for each module (e.g., `FinPlateOutputData`, `CleatAngleOutputData`) + - Repeated logic across multiple files + - No shared base classes or utilities + +5. **No Clear Separation of Concerns** + - Business logic mixed with API views + - No service layer abstraction + - Adapter logic scattered across files + +6. **Difficult to Scale** + - Adding a new module required changes in multiple files + - No auto-discovery mechanism + - Hard to maintain consistency across modules + +--- + +## Old Architecture Overview + +### Directory Structure + +``` +Osdag-web/ +├── osdag/ # Main Django app +│ ├── urls.py # 140+ lines, all module routes here +│ ├── models.py # Database models +│ ├── web_api/ # API views +│ │ ├── outputCalc_view.py # Generic output view +│ │ ├── finplate_outputView.py +│ │ ├── cleatangle_outputView.py +│ │ └── ... # One view per module +│ └── web_api/inputdata/ # Input data handlers +│ └── ... +├── osdag_api/ # Module adapters +│ ├── module_finder.py # Manual module registration +│ └── modules/ # Flat list of modules +│ ├── fin_plate_connection.py +│ ├── cleat_angle_connection.py +│ └── ... +└── osdag_web/ # Django project config + ├── settings.py + └── urls.py +``` + +### Key Files + +#### `osdag_api/module_finder.py` (Old) + +```python +# Manual imports for every module +from osdag_api.modules import ( + fin_plate_connection, + end_plate_connection, + cleat_angle_connection, + seated_angle_connection, + cover_plate_bolted_connection, + # ... more imports +) + +# Manual dictionary registration +module_dict: Dict[str, ModuleApiType] = { + 'FinPlateConnection': fin_plate_connection, + 'End-Plate-Connection': end_plate_connection, + 'Cleat-Angle-Connection': cleat_angle_connection, + # ... had to add each module manually +} + +def get_module_api(module_id: str) -> ModuleApiType: + """Return the api for the specified module""" + module = module_dict[module_id] # Simple dictionary lookup + return module +``` + +**Problems:** +- Had to import every module manually +- Had to add each module to `module_dict` manually +- Easy to forget steps when adding new modules +- No auto-discovery + +#### `osdag/urls.py` (Old) + +```python +# Individual URL route for each module +urlpatterns = [ + # ... other routes + path('calculate-output/FinPlateConnection', OutputData.as_view(), name='FinPlateConnection'), + path('calculate-output/End-Plate-Connection', EndPLateOutputData.as_view(), name='End-Plate-Connection'), + path('calculate-output/Cleat-Angle-Connection', CleatAngleOutputData.as_view(), name='Cleat-Angle-Connection'), + path('calculate-output/SeatedAngleConnection', SeatedAngleOutputData.as_view(), name='SeatedAngleConnection'), + # ... 10+ more similar routes + path('calculate-output/Tension-Member-Bolted-Design', TensionMemberBoltedOutputData.as_view()), + path('calculate-output/Simply-Supported-Beam', SimplySupportedBeamOutputData.as_view()), +] +``` + +**Problems:** +- 140+ lines of repetitive URL patterns +- Each module needed its own route +- No hierarchical organization +- Hard to maintain + +#### `osdag/web_api/outputCalc_view.py` (Old) + +```python +@method_decorator(csrf_exempt, name='dispatch') +class OutputData(APIView): + def post(self, request): + input_values = request.data + module_name = input_values.get('Module', 'FinPlateConnection') + + # Get module API from dictionary + module_api = get_module_api(module_name) + + # Generate output + output, logs = module_api.generate_output(input_values) + + return JsonResponse({"data": output, "logs": logs, "success": True}, status=201) +``` + +**Problems:** +- Generic view that worked, but no structure +- No service layer +- No separation of concerns +- Mixed with other views in same file + +--- + +## New Architecture Overview + +### Directory Structure + +``` +Osdag-web/ +├── backend/ +│ ├── config/ # Django project config (renamed from osdag_web) +│ │ ├── settings.py +│ │ └── urls.py # Only includes other apps +│ ├── apps/ +│ │ ├── core/ # Shared logic +│ │ │ ├── registry.py # BaseModuleRegistry for auto-discovery +│ │ │ ├── module_finder.py +│ │ │ └── urls.py +│ │ └── modules/ # Parent modules +│ │ ├── shear_connection/ +│ │ │ ├── registry.py # Auto-discovers submodules +│ │ │ ├── urls.py # Module-specific URLs +│ │ │ ├── views.py # ViewSet for routing +│ │ │ └── submodules/ +│ │ │ ├── fin_plate/ +│ │ │ │ ├── __init__.py # MODULE_ID + Service export +│ │ │ │ ├── adapter.py # Input/output handling +│ │ │ │ └── service.py # Business logic +│ │ │ └── cleat_angle/ +│ │ │ └── ... +│ │ └── moment_connection/ +│ │ └── ... +│ └── manage.py +└── frontend/ # Frontend (unchanged) +``` + +### Key Improvements + +#### 1. Hierarchical Module Organization + +**Parent Modules** group related submodules: +- `shear_connection/` → `fin_plate`, `cleat_angle`, `seated_angle`, `end_plate` +- `moment_connection/` → `beam_beam_end_plate`, `beam_column_end_plate`, etc. +- `tension_member/` → `bolted`, `welded` +- `flexure_member/` → `simply_supported_beam` + +#### 2. Auto-Discovery System + +**`apps/core/registry.py`** - Base registry class: + +```python +class BaseModuleRegistry: + """Base registry class for auto-discovering sub-modules""" + _registry: Dict[str, Type] = {} + _module_id_map: Dict[str, str] = {} + + @classmethod + def auto_discover(cls, package_name: str, package_path: str): + """Auto-discover sub-modules in a package""" + for _, name, _ in pkgutil.iter_modules([package_path]): + try: + mod = importlib.import_module(f'{package_name}.{name}') + if hasattr(mod, 'MODULE_ID') and hasattr(mod, 'Service'): + slug = name.replace('_', '-') + cls.register(slug, mod.MODULE_ID, mod.Service) + except ImportError: + continue +``` + +**Parent Module Registry** (e.g., `shear_connection/registry.py`): + +```python +class ShearConnectionRegistry(BaseModuleRegistry): + """Registry for shear connection sub-modules""" + pass + +# Auto-discover sub-modules +_package_name = 'apps.modules.shear_connection.submodules' +_package_path = os.path.join(os.path.dirname(__file__), 'submodules') +ShearConnectionRegistry.auto_discover(_package_name, _package_path) +``` + +**Benefits:** +- No manual imports needed +- No manual dictionary registration +- Just create the directory structure and it's discovered automatically + +#### 3. Clean URL Structure + +**`backend/config/urls.py`** - Only includes other apps: + +```python +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include('apps.core.urls')), # Core app URLs + path('api/modules/', include('apps.modules.urls')), # All module URLs + # ... auth, etc. +] +``` + +**`backend/apps/modules/urls.py`** - Aggregates parent modules: + +```python +urlpatterns = [ + path('shear-connection/', include('apps.modules.shear_connection.urls')), + path('moment-connection/', include('apps.modules.moment_connection.urls')), + path('tension-member/', include('apps.modules.tension_member.urls')), + path('flexure-member/', include('apps.modules.flexure_member.urls')), +] +``` + +**`backend/apps/modules/shear_connection/urls.py`** - Module-specific: + +```python +router = DefaultRouter() +router.register(r'', ShearConnectionViewSet, basename='shear-connection') +urlpatterns = router.urls +``` + +**Result:** Clean, hierarchical URLs: +- `/api/modules/shear-connection/fin-plate/design/` +- `/api/modules/shear-connection/cleat-angle/design/` +- `/api/modules/moment-connection/beam-column-end-plate/design/` + +#### 4. Service and Adapter Pattern + +**Separation of Concerns:** +- **`adapter.py`**: Handles I/O transformation, validation, CAD generation +- **`service.py`**: Business logic layer, error handling, response formatting + +**Benefits:** +- Clear separation of concerns +- Reusable adapter functions +- Testable service layer +- Consistent error handling + +--- + +## Adding Modules: Before vs After + +### Adding a New Module: OLD WAY + +#### Step 1: Create Module Adapter + +Create `osdag_api/modules/new_connection.py` with adapter functions. + +#### Step 2: Manual Registration + +**Edit `osdag_api/module_finder.py`:** + +```python +# Add import at top +from osdag_api.modules import new_connection # ← Manual import + +# Add to dictionary +module_dict: Dict[str, ModuleApiType] = { + 'FinPlateConnection': fin_plate_connection, + # ... existing modules + 'NewConnection': new_connection, # ← Manual registration +} +``` + +**Problems:** +- Easy to forget the import +- Easy to forget the dictionary entry +- Both steps required for module to work +- No validation if steps are missed + +#### Step 3: Add URL Route + +**Edit `osdag/urls.py`:** + +```python +urlpatterns = [ + # ... existing routes + path('calculate-output/NewConnection', NewConnectionOutputData.as_view(), name='NewConnection'), # ← Manual route +] +``` + +**Problems:** +- Had to create a view class (or reuse generic one) +- Had to add URL manually +- URL structure inconsistent across modules + +#### Step 4: Create View (if needed) + +**Create `osdag/web_api/newconnection_outputView.py`:** + +```python +class NewConnectionOutputData(APIView): + def post(self, request): + module_api = get_module_api('NewConnection') + output, logs = module_api.generate_output(request.data) + return JsonResponse({"data": output, "logs": logs, "success": True}) +``` + +**Problems:** +- Boilerplate code for each module +- No shared base class +- Inconsistent error handling + +#### Summary: OLD WAY +- **4-5 files to edit/create** +- **Manual steps in 3 different files** +- **Easy to miss steps** +- **No auto-discovery** +- **Inconsistent structure** + +--- + +### Adding a New Module: NEW WAY + +#### Step 1: Create Submodule Directory + +```bash +backend/apps/modules/shear_connection/submodules/new_connection/ +├── __init__.py +├── adapter.py +└── service.py +``` + +#### Step 2: Create `__init__.py` + +```python +MODULE_ID = 'NewConnection' +from .service import NewConnectionService as Service +``` + +**That's it!** Auto-discovery handles the rest. + +#### Step 3: Create `adapter.py` and `service.py` + +Follow the patterns in existing submodules. No manual registration needed. + +#### Summary: NEW WAY +- **3 files to create** (all in one directory) +- **Zero manual registration** +- **Auto-discovered automatically** +- **Consistent structure** +- **URL automatically available**: `/api/modules/shear-connection/new-connection/design/` + +--- + +## Detailed Comparison: Adding a Submodule + +### OLD WAY: Adding `seated_angle` to Shear Connections + +#### Files to Create/Edit: + +1. **Create** `osdag_api/modules/seated_angle_connection.py` + - Implement adapter functions + +2. **Edit** `osdag_api/module_finder.py`: + ```python + # Add import + from osdag_api.modules import seated_angle_connection + + # Add to dict + module_dict = { + # ... existing + 'SeatedAngleConnection': seated_angle_connection, + } + ``` + +3. **Edit** `osdag/urls.py`: + ```python + path('calculate-output/SeatedAngleConnection', SeatedAngleOutputData.as_view()), + ``` + +4. **Create** `osdag/web_api/seatedangle_outputView.py`: + ```python + class SeatedAngleOutputData(APIView): + def post(self, request): + # ... boilerplate + ``` + +**Total: 4 files across 3 directories** + +--- + +### NEW WAY: Adding `seated_angle` to Shear Connections + +#### Files to Create: + +1. **Create** `backend/apps/modules/shear_connection/submodules/seated_angle/__init__.py`: + ```python + MODULE_ID = 'SeatedAngleConnection' + from .service import SeatedAngleService as Service + ``` + +2. **Create** `backend/apps/modules/shear_connection/submodules/seated_angle/adapter.py` + - Implement adapter functions + +3. **Create** `backend/apps/modules/shear_connection/submodules/seated_angle/service.py` + - Implement service class + +**Total: 3 files in 1 directory** + +**Auto-discovered:** Registry automatically finds it +**URL automatically available:** `/api/modules/shear-connection/seated-angle/design/` +**No manual registration needed** + +--- + +## Key Improvements + +### 1. **Auto-Discovery vs Manual Registration** + +| Aspect | Old | New | +|--------|-----|-----| +| Registration | Manual imports + dictionary | Auto-discovered from directory structure | +| Steps Required | 2-3 manual steps | 0 (automatic) | +| Error-Prone | High (easy to miss steps) | Low (structure enforces correctness) | +| Consistency | Low (different patterns) | High (enforced by structure) | + +### 2. **URL Organization** + +| Aspect | Old | New | +|--------|-----|-----| +| Structure | Flat: `calculate-output/ModuleName` | Hierarchical: `api/modules/parent/submodule/design/` | +| Maintenance | 140+ lines in one file | Distributed across module files | +| Scalability | Poor (grows linearly) | Excellent (scales with structure) | +| Discoverability | Hard to find module URLs | Clear hierarchy | + +### 3. **Code Organization** + +| Aspect | Old | New | +|--------|-----|-----| +| Structure | Flat, scattered | Hierarchical, grouped | +| Parent Modules | None | Groups related submodules | +| Separation | Mixed concerns | Clear service/adapter pattern | +| Reusability | Low | High (shared base classes) | + +### 4. **Developer Experience** + +| Aspect | Old | New | +|--------|-----|-----| +| Adding Module | 4-5 files, 3 directories | 3 files, 1 directory | +| Learning Curve | High (need to know all files) | Low (follow pattern) | +| Error Prevention | Low (manual steps) | High (structure enforces) | +| Documentation | Scattered | Centralized in module directory | + +### 5. **Maintainability** + +| Aspect | Old | New | +|--------|-----|-----| +| Finding Code | Search across multiple directories | Clear module hierarchy | +| Understanding Structure | Requires reading multiple files | Self-documenting structure | +| Refactoring | Risky (many files to change) | Safer (isolated modules) | +| Testing | Harder (coupled code) | Easier (isolated services) | + +--- + +## Migration Path + +### Current State + +The codebase is in a **transitional state**: + +1. **New modules** use the new architecture: + - `shear_connection` submodules (fin_plate, cleat_angle, etc.) + - `moment_connection` submodules + - `tension_member` submodules + - `flexure_member` submodules + +2. **Legacy modules** still use old system: + - Some modules still in `osdag_old/` + - Old `calculate-output/` URLs were removed in the refactored backend + - `osdag_api/module_finder.py` still exists for backward compatibility + +3. **Backward Compatibility**: + - `apps/core/module_finder.py` falls back to old system + - Old URLs do not exist in the refactored backend; migrate callers to `api/modules/...` + - Gradual migration possible + +### Future State + +Once all modules are migrated: + +1. **Remove** `osdag_old/` directory +2. **Remove** old `osdag_api/` structure +3. **Remove** legacy `calculate-output/` URLs +4. **Clean up** `module_finder.py` (remove fallback logic) + +--- + +## Example: Complete Module Addition Comparison + +### Scenario: Adding a new "Gusset Plate" connection to Shear Connections + +#### OLD WAY (5 steps, 4 files): + +1. Create `osdag_api/modules/gusset_plate_connection.py` +2. Edit `osdag_api/module_finder.py` (import + dict entry) +3. Edit `osdag/urls.py` (add route) +4. Create `osdag/web_api/gussetplate_outputView.py` +5. Test (hope you didn't miss a step) + +**Time:** ~30-45 minutes +**Risk:** High (easy to miss steps) +**Files:** 4 files across 3 directories + +#### NEW WAY (3 steps, 3 files): + +1. Create `backend/apps/modules/shear_connection/submodules/gusset_plate/__init__.py` +2. Create `adapter.py` and `service.py` (copy pattern from existing submodule) +3. Test (auto-discovered, URL automatically available) + +**Time:** ~15-20 minutes +**Risk:** Low (structure enforces correctness) +**Files:** 3 files in 1 directory + +**URL automatically available:** `/api/modules/shear-connection/gusset-plate/design/` + +--- + +## Benefits Summary + +### For Developers + +✅ **Faster module addition** (50% less time) +✅ **Fewer manual steps** (0 vs 3-4) +✅ **Lower error rate** (structure prevents mistakes) +✅ **Clear patterns** (follow existing submodules) +✅ **Better organization** (everything in one place) + +### For the Codebase + +✅ **Scalable architecture** (handles growth easily) +✅ **Maintainable structure** (clear hierarchy) +✅ **Consistent patterns** (enforced by structure) +✅ **Testable code** (isolated services) +✅ **Self-documenting** (structure shows organization) + +### For the Project + +✅ **Easier onboarding** (clear structure) +✅ **Faster development** (less boilerplate) +✅ **Better quality** (structure prevents errors) +✅ **Easier maintenance** (organized code) +✅ **Future-proof** (scalable architecture) + +--- + +## Related Documentation + +- **Adding New Modules (Backend)**: See `documentation/ADDING_MODULES_AND_SUBMODULES_BACKEND.md` +- **Adding New Modules (Frontend)**: See `frontend/src/modules/shearConnection/CREATE_NEW_MODULE.md` +- **Refactoring Plan**: See `documentation/refactoring/19.11.md` + +--- + +*Last updated: Based on current architecture as of 2024* + diff --git a/documentation/general/SIMPLY_SUPPORTED_BEAM.md b/documentation/general/SIMPLY_SUPPORTED_BEAM.md new file mode 100644 index 000000000..f91d3533f --- /dev/null +++ b/documentation/general/SIMPLY_SUPPORTED_BEAM.md @@ -0,0 +1,211 @@ +### Simply Supported Beam: Django Flow and Backend Integration + +This document explains how the Simply Supported Beam module flows through Django, how the request is resolved to the module API, and how it connects to the `design_type` flexural calculations. + +--- + +## URL → View + +- Legacy endpoint (removed): `calculate-output/Simply-Supported-Beam` +- Current endpoint uses the modules API under `api/modules/.../design/`. + +```text +The current web backend no longer wires `calculate-output/*` routes. Use the refactored modules API. + +Example (shape only; module path depends on how this module is registered): +- `POST /api/modules///design/` +``` + +View handler (Django REST Framework `APIView`): + +```python +# osdag/web_api/simplysupportedbeam_outputView.py +class SimplySupportedBeamOutputData(APIView): + def post(self, request): + input_values = request.data + module_name = input_values.get('Module', 'Simply-Supported-Beam') + module_api = get_module_api(module_name) + output, logs = module_api.generate_output(input_values) + return JsonResponse({"data": output, "logs": logs, "success": True}, safe=False, status=201) +``` + +--- + +## Module Resolution + +`get_module_api` maps the module key specified in the request (`"Module": "Simply-Supported-Beam"`) to the correct module adapter. + +```python +# osdag_api/module_finder.py +module_dict = { + ... + 'Simply-Supported-Beam': simply_supported_beam, +} + +def get_module_api(module_id: str): + return module_dict[module_id] +``` + +The module is also listed in the supported modules: + +```python +# osdag_api/__init__.py +developed_modules = [ + ..., + "Simply-Supported-Beam" +] +``` + +--- + +## Module Adapter → Flexure Engine + +The Simply Supported Beam adapter is in `osdag_api/modules/simply_supported_beam.py`. It: +- Validates/normalizes request inputs +- Maps frontend keys to the exact keys expected by the flexure engine +- Instantiates `design_type.flexural_member.flexure.Flexure` +- Calls the engine to get results and formats them as a flat JSON dictionary + +Key functions: + +```python +# osdag_api/modules/simply_supported_beam.py +def get_required_keys() -> List[str]: + return [ + "Module","Member.Profile","Member.Designation","Material","Member.Material", + "Design.Design_Method","Design.Allowable_Class","Design.Effective_Area_Parameter", + "Length.Overwrite","Bearing.Length","Load.Shear","Load.Moment","Member.Length", + "Support.Type","Torsion.restraint","Warping.restraint", + ] + +def create_from_input(input_values: Dict[str, Any]) -> Flexure: + module = create_module() # Flexure() + design_dict = { + 'Module': input_values.get('Module', 'Simply-Supported-Beam'), + 'Member.Profile': input_values.get('Member.Profile', ''), + 'Member.Designation': processed_member_designation, # array + 'Material': input_values.get('Material', ''), + 'Member.Material': input_values.get('Member.Material', ''), + 'Flexure.Type': input_values.get('Flexure.Type', 'Major Laterally Supported'), + 'Design.Allowable_Class': input_values.get('Design.Allowable_Class', ''), + 'Design.Effective_Area_Parameter': input_values.get('Design.Effective_Area_Parameter', ''), + 'Length.Overwrite': input_values.get('Length.Overwrite', ''), + 'Bearing.Length': input_values.get('Bearing.Length', ''), + 'Load.Shear': input_values.get('Load.Shear', ''), + 'Load.Moment': input_values.get('Load.Moment', ''), + 'Member.Length': input_values.get('Member.Length', ''), + 'Flexure.Support': input_values.get('Flexure.Support', ''), + 'Torsion.restraint': input_values.get('Torsion.restraint', ''), + 'Warping.restraint': input_values.get('Warping.restraint', ''), + 'Loading.Condition': 'Normal', + } + module.set_input_values(design_dict) + return module + +def generate_output(input_values: Dict[str, Any]): + module = create_from_input(input_values) + raw_output_text = module.output_values(True) + raw_output_spacing = module.spacing(True) + # Flatten to { key: {key,label,val} } + ... + return output, logs +``` + +--- + +## Flexure Calculations (`design_type`) + +The actual flexural computations are implemented in `design_type/flexural_member/flexure.py` in class `Flexure`. The module adapter above calls: +- `Flexure.set_input_values(design_dict)` +- `Flexure.output_values(True)` and `Flexure.spacing(True)` to obtain output rows + +```python +# design_type/flexural_member/flexure.py +class Flexure(Member): + def __init__(self): + super(Flexure, self).__init__() + self.logs = [] + ... # extensive flexural design logic +``` + +--- + +## Input Options API (for populating UI) + +For dropdowns and lists used by the Simply Supported Beam UI, see: + +```python +# osdag/web_api/inputdata/simply_supported_beam_input.py +class SimplySupportedBeamInputData(InputDataBase): + def process(self, **kwargs): + response['beamList'] = list(Beams.objects.values_list('Designation', flat=True)) + response['columnList'] = list(Columns.objects.values_list('Designation', flat=True)) + response['sectionProfileList'] = ["Beams and Columns"] + response['materialList'] = Material + CustomMaterials (+ 'Custom') + response['designMethodList'] = ["Limit State Design", "Working Stress Design"] + response['allowableClassList'] = ["Plastic", "Compact", "Semi-Compact"] + response['beamSupportTypeList'] = [ + "Major Laterally Supported", + "Minor Laterally Unsupported", + "Major Laterally Unsupported", + ] + response['torsionalRestraintList'] = [...] + response['warpingRestraintList'] = [...] + return Response(response, status=200) +``` + +--- + +## Minimal Request/Response + +- (Removed) POST `calculate-output/Simply-Supported-Beam` + +Request body must include at least (keys shown are the module’s required inputs): + +```json +{ + "Module": "Simply-Supported-Beam", + "Member.Profile": "Beams and Columns", + "Member.Designation": ["ISMB 300"], + "Material": "E 250 (Fe 410 W)A", + "Member.Material": "E 250 (Fe 410 W)A", + "Design.Design_Method": "Limit State Design", + "Design.Allowable_Class": "Plastic", + "Design.Effective_Area_Parameter": "1.0", + "Length.Overwrite": "NA", + "Bearing.Length": "NA", + "Load.Shear": "100", + "Load.Moment": "50", + "Member.Length": "3000", + "Support.Type": "Simply Supported", + "Flexure.Type": "Major Laterally Supported", + "Torsion.restraint": "Fully Restrained", + "Warping.restraint": "Both flanges fully restrained" +} +``` + +Response (shape): + +```json +{ + "data": { + "": {"key": "", "label": "