diff --git a/docs/en/assets/css/fec.css b/docs/en/assets/css/fec.css new file mode 100644 index 000000000..1db65677e --- /dev/null +++ b/docs/en/assets/css/fec.css @@ -0,0 +1,620 @@ +.p-5{padding:3rem!important} + +.row { + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + display: flex; + flex-wrap: wrap; + margin-top: calc(var(--bs-gutter-y) * -1); + margin-right: calc(var(--bs-gutter-x) * -.5); + margin-left: calc(var(--bs-gutter-x) * -.5); +} +.row > * { + flex-shrink: 0; + width: 100%; + max-width: 100%; + padding-right: calc(var(--bs-gutter-x) * .5); + padding-left: calc(var(--bs-gutter-x) * .5); + margin-top: var(--bs-gutter-y); +} + +.col { + flex: 1 0 0; +} + +.row-cols-auto > * { + flex: 0 0 auto; + width: auto; +} + +.row-cols-1 > * { + flex: 0 0 auto; + width: 100%; +} + +.row-cols-2 > * { + flex: 0 0 auto; + width: 50%; +} + +.row-cols-3 > * { + flex: 0 0 auto; + width: 33.3333333333%; +} + +.row-cols-4 > * { + flex: 0 0 auto; + width: 25%; +} + +.row-cols-5 > * { + flex: 0 0 auto; + width: 20%; +} + +.row-cols-6 > * { + flex: 0 0 auto; + width: 16.6666666667%; +} + + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background: #fff; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); + transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} + +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eeeeee; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + vertical-align: middle; + cursor: pointer; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +.row { + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + display: flex; + flex-wrap: wrap; + margin-top: calc(var(--bs-gutter-y) * -1); + margin-right: calc(var(--bs-gutter-x) * -.5); + margin-left: calc(var(--bs-gutter-x) * -.5); +} +.row > * { + flex-shrink: 0; + width: 100%; + max-width: 100%; + padding-right: calc(var(--bs-gutter-x) * .5); + padding-left: calc(var(--bs-gutter-x) * .5); + margin-top: var(--bs-gutter-y); +} + +.col { + flex: 1 0 0; +} + +.row-cols-auto > * { + flex: 0 0 auto; + width: auto; +} + +.row-cols-1 > * { + flex: 0 0 auto; + width: 100%; +} + +.row-cols-2 > * { + flex: 0 0 auto; + width: 50%; +} + +.row-cols-3 > * { + flex: 0 0 auto; + width: 33.3333333333%; +} + +.row-cols-4 > * { + flex: 0 0 auto; + width: 25%; +} + +.row-cols-5 > * { + flex: 0 0 auto; + width: 20%; +} + +.row-cols-6 > * { + flex: 0 0 auto; + width: 16.6666666667%; +} + +.font-weight-bold{font-weight:700!important} +.font-weight-bolder{font-weight:bolder!important} \ No newline at end of file diff --git a/docs/en/assets/custom.css b/docs/en/assets/custom.css new file mode 100644 index 000000000..a23be60f1 --- /dev/null +++ b/docs/en/assets/custom.css @@ -0,0 +1,301 @@ +iframe[src*="ads-iframe"] { + display: none; +} + +iframe[id*="dsq-app"] { + display: none; +} + +iframe[id*="dsq-app"][src*="comments"] { + display: block; +} + +:root { + --md-admonition-icon--give-money: url('data:image/svg+xml;charset=utf-8,') +} + +.md-grid { + max-width: 1600px; +} + + +/* Preserve link color */ +.md-banner a, +.md-banner a:focus, +.md-banner a:hover { + color: currentColor; +} + +/* Don't wrap name of blog article */ +.md-banner strong { + white-space: nowrap; +} + +.md-banner { + color: #fafafa; + background-color: #4caf50; + cursor: pointer; +} + +.md-banner:hover, +.md-banner:focus { + color: #fafafa; + background-color: #087f23; +} + +/* Twitter icon */ +.md-banner .twitter { + margin-left: .2em; + color: #00acee; +} + +.md-typeset ul { + display: block; +} + +.md-header-nav__title { + padding-top: 1px; + padding-left: 0; +} + +.md-nav__item:hover { + background-color: rgba(63, 81, 181, 0.1); +} + +.md-nav__item--nested { + background: none !important; +} + +.md-tabs__item:hover { + background: #5564C3; + border-bottom: 4px solid #fff; + transform: scaleY(1); +} + +.md-tabs__item--active { + border-bottom: 4px solid #fff; + color: inherit; + opacity: 1; +} + +.md-header__button.md-logo img { + width: auto !important; + height: 43px !important; +} + +.pure-material-button-contained { + position: relative; + display: inline-block; + box-sizing: border-box; + border: none; + border-radius: 4px; + padding: 0 16px; + min-width: 64px; + height: 30px; + vertical-align: middle; + text-align: center; + text-overflow: ellipsis; + text-transform: uppercase; + color: rgb(var(--pure-material-onprimary-rgb, 255, 255, 255)) !important; + background-color: rgb(var(--pure-material-primary-rgb, 33, 150, 243)); + box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + font-family: var(--pure-material-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system), serif; + font-size: 14px; + font-weight: 500; + line-height: 30px; + overflow: hidden; + outline: none; + cursor: pointer; + transition: box-shadow 0.2s; +} + +.pure-material-button-contained::-moz-focus-inner { + border: none; +} + +/* Overlay */ +.pure-material-button-contained::before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: rgb(var(--pure-material-onprimary-rgb, 255, 255, 255)); + opacity: 0; + transition: opacity 0.2s; +} + +/* Ripple */ +.pure-material-button-contained::after { + content: ""; + position: absolute; + left: 50%; + top: 50%; + border-radius: 50%; + padding: 50%; + width: 32px; /* Safari */ + height: 32px; /* Safari */ + background-color: rgb(var(--pure-material-onprimary-rgb, 255, 255, 255)); + opacity: 0; + transform: translate(-50%, -50%) scale(1); + transition: opacity 1s, transform 0.5s; +} + +/* Hover, Focus */ +.pure-material-button-contained:hover, +.pure-material-button-contained:focus { + box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12); +} + +.pure-material-button-contained:hover::before { + opacity: 0.08; +} + +.pure-material-button-contained:focus::before { + opacity: 0.24; +} + +.pure-material-button-contained:hover:focus::before { + opacity: 0.3; +} + +/* Active */ +.pure-material-button-contained:active { + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12); +} + +.pure-material-button-contained:active::after { + opacity: 0.32; + transform: translate(-50%, -50%) scale(0); + transition: transform 0s; +} + +/* Disabled */ +.pure-material-button-contained:disabled { + color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38); + background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.12); + box-shadow: none; + cursor: initial; +} + +.pure-material-button-contained:disabled::before { + opacity: 0; +} + +.pure-material-button-contained:disabled::after { + opacity: 0; +} + +.md-typeset .admonition { + margin-top: 0 !important; +} + +.md-typeset .admonition.give-money, +.md-typeset details.give-money { + border-color: #448aff; +} + +.md-typeset .give-money > .admonition-title, +.md-typeset .give-money > summary { + background-color: rgba(68,138,255,.1); +} + +.md-typeset .give-money > .admonition-title::before, +.md-typeset .give-money > summary::before { + background-color: rgba(68,138,255); + -webkit-mask-image: var(--md-admonition-icon--give-money); + mask-image: var(--md-admonition-icon--give-money); +} + +.mdx-container { + padding-top: 1rem; + background: url("data:image/svg+xml;utf8,") no-repeat bottom, linear-gradient(180deg, var(--md-primary-fg-color), #a63fd9 99%, var(--md-default-bg-color) 0) +} + +[data-md-color-scheme=slate] .mdx-container { + background: url("data:image/svg+xml;utf8,") no-repeat bottom, linear-gradient(180deg, var(--md-primary-fg-color), #a63fd9 99%, var(--md-default-bg-color) 0) +} + +.mdx-hero { + margin: 0 .8rem; + color: var(--md-primary-bg-color) +} + +.mdx-hero h1 { + margin-bottom: 1rem; + color: currentColor; + font-weight: 700 +} + +/* Styles for custom pages */ + +table { + *border-collapse: collapse; /* IE7 and lower */ + border-spacing: 0; + width: 100%; +} + +.bordered { + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 1px 1px #ccc; + -moz-box-shadow: 0 1px 1px #ccc; + box-shadow: 0 1px 1px #ccc; +} + +.bordered td, .bordered th { + border-left: 1px solid #ccc; + border-top: 1px solid #ccc; + padding: 10px; + text-align: left; +} + +.bordered th { + background-color: #3F51B5; + color: #FFFFFF; + -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.8) inset; + -moz-box-shadow:0 1px 0 rgba(255,255,255,.8) inset; + box-shadow: 0 1px 0 rgba(255,255,255,.8) inset; + border-top: none; + text-shadow: 0 1px 0 rgba(255,255,255,.5); +} + +.bordered tr:hover { + -o-transition: all 0.1s ease-in-out; + -webkit-transition: all 0.1s ease-in-out; + -moz-transition: all 0.1s ease-in-out; + -ms-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +} + +.bordered th:first-child { + -moz-border-radius: 6px 0 0 0; + -webkit-border-radius: 6px 0 0 0; + border-radius: 6px 0 0 0; +} + +.bordered th:last-child { + -moz-border-radius: 0 6px 0 0; + -webkit-border-radius: 0 6px 0 0; + border-radius: 0 6px 0 0; +} + +.bordered th:only-child{ + -moz-border-radius: 6px 6px 0 0; + -webkit-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} + +.bordered tr:last-child td:first-child { + -moz-border-radius: 0 0 0 6px; + -webkit-border-radius: 0 0 0 6px; + border-radius: 0 0 0 6px; +} + +.bordered tr:last-child td:last-child { + -moz-border-radius: 0 0 6px 0; + -webkit-border-radius: 0 0 6px 0; + border-radius: 0 0 6px 0; +} \ No newline at end of file diff --git a/docs/en/assets/images/icon.png b/docs/en/assets/images/icon.png new file mode 100644 index 000000000..9376d1b61 Binary files /dev/null and b/docs/en/assets/images/icon.png differ diff --git a/docs/en/assets/images/illustration.png b/docs/en/assets/images/illustration.png new file mode 100644 index 000000000..69f739c05 Binary files /dev/null and b/docs/en/assets/images/illustration.png differ diff --git a/docs/en/assets/images/loading.gif b/docs/en/assets/images/loading.gif new file mode 100644 index 000000000..1448c29d5 Binary files /dev/null and b/docs/en/assets/images/loading.gif differ diff --git a/docs/en/assets/images/logo.png b/docs/en/assets/images/logo.png new file mode 100644 index 000000000..eadb517c7 Binary files /dev/null and b/docs/en/assets/images/logo.png differ diff --git a/docs/en/assets/js/crc.min.js b/docs/en/assets/js/crc.min.js new file mode 100644 index 000000000..c7180330e --- /dev/null +++ b/docs/en/assets/js/crc.min.js @@ -0,0 +1,147 @@ +function CRC16_CCITT(charach){ + var crc = 0xFFFF; + var polynomial = 0x1021; + var i=0, k=0, byte_val, bit, c15; + + for (let i = 0; i < charach.length; i++) { + byte_val = charach[i]; + + for (k=0; k < 8; k++) { + bit = ((byte_val >> (7 - k) & 1) == 1); + c15 = ((crc >> 15 & 1) == 1); + crc <<= 1; + if (c15 ^ bit) { + crc ^=polynomial; + } + } + } + crc &= 0xffff; + + return crc.toString(16); +} + +/** + * [js-crc]{@link https://github.com/emn178/js-crc} + * + * @namespace crc + * @version 0.2.0 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2015-2017 + * @license MIT + */ +/*jslint bitwise: true */ +(function () { + 'use strict'; + + var root = typeof window === 'object' ? window : {}; + var NODE_JS = !root.JS_CRC_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; + if (NODE_JS) { + root = global; + } + var COMMON_JS = !root.JS_CRC_NO_COMMON_JS && typeof module === 'object' && module.exports; + var AMD = typeof define === 'function' && define.amd; + var ARRAY_BUFFER = !root.JS_CRC_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; + var HEX_CHARS = '0123456789abcdef'.split(''); + + var Modules = [ + { + name: 'crc32', + polynom: 0xEDB88320, + initValue: -1, + bytes: 4 + }, + { + name: 'crc16', + polynom: 0xA001, + initValue: 0, + bytes: 2 + }, + { + name: 'crc16ccit', + polynom: 0x8408, + initValue: 0, + bytes: 2 + } + ]; + + var i, j, k, b; + for (i = 0; i < Modules.length; ++i) { + var m = Modules[i]; + m.method = (function (m) { + return function (message) { + return crc(message, m); + }; + })(m); + m.table = []; + for (j = 0; j < 256; ++j) { + b = j; + for (k = 0; k < 8; ++k) { + b = b & 1 ? m.polynom ^ (b >>> 1) : b >>> 1; + } + m.table[j] = b >>> 0; + } + } + + var crc = function (message, module) { + var notString = typeof message !== 'string'; + if (notString && ARRAY_BUFFER && message instanceof ArrayBuffer) { + message = new Uint8Array(message); + } + + var crc = module.initValue, code, i, length = message.length, table = module.table; + if (notString) { + for (i = 0; i < length; ++i) { + crc = table[(crc ^ message[i]) & 0xFF] ^ (crc >>> 8); + } + } else { + for (i = 0; i < length; ++i) { + code = message.charCodeAt(i); + if (code < 0x80) { + crc = table[(crc ^ code) & 0xFF] ^ (crc >>> 8); + } else if (code < 0x800) { + crc = table[(crc ^ (0xc0 | (code >> 6))) & 0xFF] ^ (crc >>> 8); + crc = table[(crc ^ (0x80 | (code & 0x3f))) & 0xFF] ^ (crc >>> 8); + } else if (code < 0xd800 || code >= 0xe000) { + crc = table[(crc ^ (0xe0 | (code >> 12))) & 0xFF] ^ (crc >>> 8); + crc = table[(crc ^ (0x80 | ((code >> 6) & 0x3f))) & 0xFF] ^ (crc >>> 8); + crc = table[(crc ^ (0x80 | (code & 0x3f))) & 0xFF] ^ (crc >>> 8); + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++i) & 0x3ff)); + crc = table[(crc ^ (0xf0 | (code >> 18))) & 0xFF] ^ (crc >>> 8); + crc = table[(crc ^ (0x80 | ((code >> 12) & 0x3f))) & 0xFF] ^ (crc >>> 8); + crc = table[(crc ^ (0x80 | ((code >> 6) & 0x3f))) & 0xFF] ^ (crc >>> 8); + crc = table[(crc ^ (0x80 | (code & 0x3f))) & 0xFF] ^ (crc >>> 8); + } + } + } + crc ^= module.initValue; + + var hex = ''; + if (module.bytes > 2) { + hex += HEX_CHARS[(crc >> 28) & 0x0F] + HEX_CHARS[(crc >> 24) & 0x0F] + + HEX_CHARS[(crc >> 20) & 0x0F] + HEX_CHARS[(crc >> 16) & 0x0F]; + } + hex += HEX_CHARS[(crc >> 12) & 0x0F] + HEX_CHARS[(crc >> 8) & 0x0F] + + HEX_CHARS[(crc >> 4) & 0x0F] + HEX_CHARS[crc & 0x0F]; + return hex; + }; + + var exports = {}; + for (i = 0;i < Modules.length;++i) { + var m = Modules[i]; + exports[m.name] = m.method; + } + if (COMMON_JS) { + module.exports = exports; + } else { + for (i = 0;i < Modules.length;++i) { + var m = Modules[i]; + root[m.name] = m.method; + } + if (AMD) { + define(function() { + return exports; + }); + } + } +})(); \ No newline at end of file diff --git a/docs/en/assets/js/dataset.js b/docs/en/assets/js/dataset.js new file mode 100644 index 000000000..b216c27b6 --- /dev/null +++ b/docs/en/assets/js/dataset.js @@ -0,0 +1,563 @@ +/** + * Convert big-endian hex to little-endian hex and vice-versa + */ +function setEndianConvert(value) { + let result = ''; + if (value.length % 2) { + value = '0' + String(value); + } + for (let n = 0; n < value.length; n += 4) { + const str = value.substring(n, n + 4); + result += str.replace(/(..)(..)?/g, "$2$1"); + } + return result; +} + +/** + * Check if hex is little-endian. Example: 5216 = true; 1652 = false + */ +function isLittleEndian(value) { + if (value.length % 2) { + value = '0' + String(value); + } + let array = hexToDecimalArray(value.substring(0, 4)); + if (array[0] === array[1]) { + return isLittleEndian(value.substring(4)); + } + return array[0] > array[1]; +} + +/** + * Convert hex string to decimal array. Example: FF -> 255 + */ +function hexToDecimalArray(hex) { + if (!hex.match(/^[0-9a-fA-F]+$/)) { + throw new Error('is not a hex string.'); + } + if (hex.length % 2 !== 0) { + hex = '0' + hex; + } + const bytes = []; + for (let n = 0; n < hex.length; n += 2) { + const code = parseInt(hex.substring(n, n + 2), 16); + bytes.push(code); + } + return bytes; +} + +/** + * Convert ascii representation to hex string. Example: Ex -> 4578 + */ +function asciiToHex(str) { + const arr1 = []; + let n = 0, l = str.length; + for (; n < l; n++) { + const hex = Number(str.charCodeAt(n)).toString(16); + arr1.push(hex); + } + return arr1.join(''); +} + +/** + * Convert hex string to ascii representation. Example: 4578 -> Ex + */ +function hexToAscii(str1) { + const hex = str1.toString(); + let str = ''; + for (let n = 0; n < hex.length; n += 2) { + str += String.fromCharCode(parseInt(hex.substring(n, n + 2), 16)); + } + return str; +} + +/** + * Convert hex value to readable array. Example: 0x45,0x78 -> 45,78 + */ +function parseHexValueToArray(str) { + const list = str.replace(/,/g, " ").replace(/[^A-Fa-f0-9 ]/g, "").split(' '); + const result = []; + + for (let i = 0; i < list.length; i++) { + if (list[i].length === 0) + continue; + result.push(list[i].substring(1)); + } + return result; +} + +/** + * Convert hex value to readable string. Example: 0x45,0x78 -> 4578 + */ +function parseHexValueToString(str) { + const list = str.replace(/,/g, " ").replace(/[^A-Fa-f0-9 ]/g, "").split(' '); + let result = ""; + + for (let i = 0; i < list.length; i++) { + if (list[i].length === 0) + continue; + result += list[i].substring(1); + } + return result; +} + +/** + * Convert readable string to hex value. Example: 4578 or 45 78 -> 0x45,0x78 + */ +function parseStringValueToHex(str) { + const matches = str.matchAll(/[^\s]{1,2}/g); + let result = ""; + for (const match of matches) { + result += "0x" + match + ","; + } + return result.substring(0, result.length - 1); +} + +/** + * Clean hex value. Example: 45 78 -> 4548 + */ +function cleanHexValue(str) { + const matches = str.matchAll(/[^\s]{1,2}/g); + let result = ""; + for (const match of matches) { + result += match; + } + return result; +} + +/** + * Convert hex value to readable string array delimited by 16 bytes. Example: 4578... or 45 78... -> 45 78 ... /n + */ +function parseValue(byteStream) { + let resultValue = ""; + let bytes; + + if (!Array.isArray(byteStream)) { + bytes = byteStream.match(/[^\s]{1,2}/g); + } else { + bytes = byteStream; + } + + for (let index = 0; index < bytes.length; index++) { + if (((index % 16) === 0) && (index > 0)) { + resultValue += "\n"; + } + + let value = bytes[index].toString(16).toUpperCase(); + if (value.length < 2) { + value = '0' + value; + } + resultValue += value + " "; + } + return resultValue; +} + +function addToDataset(dataset, info) { + dataset.value += info; +} + +function generateHeader(dataset) { + addToDataset(dataset, "\n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " 000000000\n"); + + const rows = document.getElementById('dataParams').rows; + if (rows.length > 3) { + let previousRow = ""; + for (let i = 1; i < rows.length - 1; i++) { + if (rows[i].id && previousRow !== rows[i].id) { + previousRow = rows[i].id; + addToDataset(dataset, " "); + addToDataset(dataset, doCreateParam(previousRow, false)); + addToDataset(dataset, "\n"); + } + } + } + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); +} + +function generateFooter(dataset) { + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, " \n"); + addToDataset(dataset, "\n"); +} + +function generateBody(dataset, data) { + addToDataset(dataset, " \n"); + addToDataset(dataset, " "); + + if (document.getElementById('uncompressedBytes').value !== '0' && + document.getElementById('compressedBytes').value !== '0') { + addToDataset(dataset, data); + } + + addToDataset(dataset, "\n"); + addToDataset(dataset, " \n"); +} + +function doGenerate() { + doOriginal(); + const dataset = document.getElementById('dataset'); + dataset.value = ""; + + const data = document.getElementById('data'); + + generateHeader(dataset); + generateBody(dataset, data); + generateFooter(dataset); + + const byteStream = new Uint8Array(dataset.value.length); + document.getElementById("generatedData").style.display = "table"; + return byteStream; +} + +function getRandomInt(max) { + return Math.floor(Math.random() * max); +} + +function doClearParams() { + while (document.getElementById('dataParams').rows.length > 3) { + const rows = document.getElementById('dataParams').rows; + for (let i = 0; i < rows.length; i++) { + if (rows[i].id) { + document.getElementById('dataParams').deleteRow(i); + } + } + } +} + +function doClearParsedParams() { + document.getElementById("parsedCompressedParams").style.display = "none"; + + while (document.getElementById('parsedCompressedParams').rows.length > 3) { + const rows = document.getElementById('parsedCompressedParams').rows; + for (let i = 0; i < rows.length; i++) { + if (rows[i].id) { + document.getElementById('parsedCompressedParams').deleteRow(i); + } + } + } +} + +function doClearFields() { + doClearParams(); + + document.getElementById('data').value = ""; + document.getElementById('dataset').value = ""; + + document.getElementById('isCompressed').checked = false; + document.getElementById('content').value = '0'; + document.getElementById('uncompressedBytes').value = '0'; + document.getElementById('compressedBytes').value = '0'; + + document.getElementById("compressedData").style.display = "none"; + document.getElementById("generatedData").style.display = "none"; + + doClearParsedParams(); +} + +function deleteRow(row, shouldBreak) { + const rows = document.getElementById('dataParams').rows; + for (let i = 0; i < rows.length; i++) { + if (rows[i].id === row) { + document.getElementById('dataParams').deleteRow(i); + if (shouldBreak) { + return; + } + deleteRow(row, true); + } + } +} + +function doCreateParsedParam(row, showDialog) { + let result = ''; + let raw = cleanHexValue(document.getElementById('parsedParam_' + row).value); + if (raw) { + doCRC32Calc(row); + result = asciiToHex(document.getElementById('parsedVersion_' + row).value) + raw + + document.getElementById("crc32_" + row).value; + if (showDialog) { + window.prompt("Parameter", result); + } + } + return result; +} + +function doCRC32Calc(row) { + let raw = cleanHexValue(document.getElementById('parsedParam_' + row).value); + if (document.getElementById('parsedVersion_' + row).value && raw) { + document.getElementById('crc32_' + row).value = crc32(hexToDecimalArray( + asciiToHex(document.getElementById('parsedVersion_' + row).value) + raw)).toUpperCase(); + } +} + +function doCreateParam(row, showDialog) { + let result = ''; + let raw = cleanHexValue(document.getElementById('param_' + row).value); + if (raw) { + doCRC16Calc(row); + result = raw + asciiToHex(document.getElementById('version_' + row).value.substring(0, 2)) + + document.getElementById("crc16_" + row).value; + if (document.getElementById('littleEndian_' + row).checked && !isLittleEndian(raw)) { + result = setEndianConvert(result); + } + if (showDialog) { + window.prompt("Parameter", parseStringValueToHex(result)); + } + } + return parseStringValueToHex(result); +} + +function doCRC16Calc(row) { + let raw = cleanHexValue(document.getElementById('param_' + row).value); + if (document.getElementById('version_' + row).value && raw) { + let hexValue = CRC16_CCITT(hexToDecimalArray( + raw + asciiToHex(document.getElementById('version_' + row).value.substring(0, 2)))).toUpperCase(); + if (isLittleEndian(raw)) { + hexValue = CRC16_CCITT(hexToDecimalArray( + raw + setEndianConvert(asciiToHex(document.getElementById('version_' + row).value.substring(0, 2))))).toUpperCase() + } + document.getElementById('crc16_' + row).value = hexValue; + } +} + +function addNewParam() { + const tableRef = document.getElementById('dataParams'); + let newRow = tableRef.insertRow(tableRef.rows.length - 1); + const randomName = getRandomInt(10000); + let html = "" + (tableRef.rows.length - 2) / 2 + ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + newRow.innerHTML = html; + newRow.id = randomName; + + newRow = tableRef.insertRow(tableRef.rows.length - 1); + html = ""; + html += ""; + html += ""; + html += "Bytes in little-endian format:
"; + html += "Version of hex:
"; + html += "CRC16 sum:
"; + html += ""; + html += ""; + newRow.innerHTML = html; + newRow.id = randomName; + return randomName; +} + +function addNewParsedParam() { + const tableRef = document.getElementById('parsedCompressedParams'); + let newRow = tableRef.insertRow(tableRef.rows.length - 1); + const randomName = getRandomInt(10000); + let html = "" + (tableRef.rows.length - 2) / 2 + ""; + html += ""; + html += ""; + html += ""; + html += ""; + newRow.innerHTML = html; + newRow.id = randomName; + + newRow = tableRef.insertRow(tableRef.rows.length - 1); + html = ""; + html += ""; + html += ""; + html += "Version of hex:
"; + html += "CRC32 sum:
"; + html += ""; + html += ""; + newRow.innerHTML = html; + newRow.id = randomName; + + return randomName; +} + +function handleUpload() { + const files = document.getElementById('uploadBinaryButton').files; + const reader = new FileReader(); + const parser = new DOMParser(); + doClearFields(); + + reader.onload = function () { + const xmlDoc = parser.parseFromString(new TextDecoder().decode(reader.result), "text/xml"); + const errorNode = xmlDoc.querySelector('parsererror'); + + if (errorNode) { + const byteStream = new Uint8Array(reader.result); + document.getElementById("compressedData").style.display = "table"; + document.getElementById('data').value = parseValue(byteStream); + } else { + if (xmlDoc.querySelector('MESSAGE')) { + const parameterAbstract = xmlDoc.querySelectorAll("PARAMETER_DATA"); + parameterAbstract.forEach(a => { + const randomName = addNewParam(); + document.getElementById('diagAddr_' + randomName).defaultValue = a.getAttribute("DIAGNOSTIC_ADDRESS"); + document.getElementById('flashOffset_' + randomName).defaultValue = a.getAttribute("START_ADDRESS"); + document.getElementById('datasetName_' + randomName).defaultValue = a.getAttribute("ZDC_NAME"); + document.getElementById('datasetVersion_' + randomName).defaultValue = a.getAttribute("ZDC_VERSION"); + document.getElementById('login_' + randomName).defaultValue = a.getAttribute("LOGIN"); + document.getElementById('datasetType_' + randomName).defaultValue = a.getAttribute("DSD_TYPE"); + document.getElementById('fileName_' + randomName).defaultValue = a.getAttribute("FILENAME"); + if (a.textContent.length !== 0) { + const raw = parseHexValueToString(a.textContent.trim()); + if (isLittleEndian(raw)) { + document.getElementById('littleEndian_' + randomName).checked = true; + document.getElementById("version_" + randomName).value = hexToAscii(setEndianConvert(raw.substring(raw.length - 8, raw.length - 4))); + document.getElementById("crc16_" + randomName).value = setEndianConvert(raw.substring(raw.length - 4, raw.length)); + } else { + document.getElementById("version_" + randomName).value = hexToAscii(raw.substring(raw.length - 8, raw.length - 4)); + document.getElementById("crc16_" + randomName).value = raw.substring(raw.length - 4, raw.length); + } + document.getElementById("param_" + randomName).value = parseValue(raw.substring(0, raw.length - 8)); + } + }); + + if (xmlDoc.querySelector('COMPRESSED_DATA') && + xmlDoc.querySelector('COMPRESSED_DATA').getAttribute("BYTES_UNCOMPRESSED") !== '0' && + xmlDoc.querySelector('COMPRESSED_DATA').getAttribute("BYTES_COMPRESSED") !== '0') { + document.getElementById("compressedData").style.display = "table"; + document.getElementById('isCompressed').checked = true; + document.getElementById('content').value = xmlDoc.querySelector('COMPRESSED_DATA').getAttribute("CONTENT"); + document.getElementById('uncompressedBytes').value = xmlDoc.querySelector('COMPRESSED_DATA').getAttribute("BYTES_UNCOMPRESSED"); + document.getElementById('compressedBytes').value = xmlDoc.querySelector('COMPRESSED_DATA').getAttribute("BYTES_COMPRESSED"); + document.getElementById('data').value = xmlDoc.querySelector('COMPRESSED_DATA').textContent.trim(); + } + } else if (xmlDoc.querySelector('SW-CNT')) { + document.getElementById('diagAddr').defaultValue = ''; + document.getElementById('flashOffset').defaultValue = xmlDoc.querySelector('START-ADR').textContent.trim(); + document.getElementById('datasetName').defaultValue = xmlDoc.querySelector('DATEN-NAME').textContent.trim(); + document.getElementById('datasetVersion').defaultValue = xmlDoc.querySelector('CNT-VERSION-INHALT').textContent.trim(); + document.getElementById('login').defaultValue = xmlDoc.querySelector('LOGIN').textContent.trim(); + document.getElementById('datasetType').defaultValue = ''; + document.getElementById('fileName').defaultValue = xmlDoc.querySelector('CNT-DATEI').textContent.trim(); + document.getElementById('data').value = xmlDoc.querySelector('DATEN').textContent.trim(); + } else { + window.alert("Uploaded file is not dataset file"); + } + } + } + reader.readAsArrayBuffer(files[0]); +} + +function doDecode() { + if (document.getElementById('uncompressedBytes').value !== '0' && + document.getElementById('compressedBytes').value !== '0' && + document.getElementById('isCompressed').checked) { + document.getElementById('data').value = atob(document.getElementById('data').value); + document.getElementById('isCompressed').checked = false; + document.getElementById('uncompressedBytes').value = document.getElementById('data').value.length; + } +} + +function doOriginal() { + if (document.getElementById('uncompressedBytes').value !== '0' && + document.getElementById('compressedBytes').value !== '0' && + !document.getElementById('isCompressed').checked) { + document.getElementById('data').value = btoa(unescape(encodeURIComponent(document.getElementById('data').value))); + document.getElementById('isCompressed').checked = true; + document.getElementById('compressedBytes').value = document.getElementById('data').value.length; + } +} + +function doParseUncompressed() { + if (document.getElementById('uncompressedBytes').value !== '0' && + document.getElementById('compressedBytes').value !== '0') { + doDecode(); + const regexp = /[\s\S]*?<\/SW-CNT>/g; + const matches = document.getElementById('data').value.matchAll(regexp); + for (const match of matches) { + const xmlDoc = new DOMParser().parseFromString(match[0], "text/xml"); + const randomName = addNewParsedParam(); + document.getElementById("parsedCompressedParams").style.display = "table"; + document.getElementById('parsedFlashOffset_' + randomName).defaultValue = xmlDoc.querySelector('START-ADR').textContent.trim(); + document.getElementById('parsedDatasetName_' + randomName).defaultValue = xmlDoc.querySelector('DATEN-NAME').textContent.trim(); + document.getElementById('parsedDatasetVersion_' + randomName).defaultValue = xmlDoc.querySelector('CNT-VERSION-INHALT').textContent.trim(); + document.getElementById('parsedFileName_' + randomName).defaultValue = xmlDoc.querySelector('CNT-DATEI').textContent.trim(); + + const data = xmlDoc.querySelector('DATEN').textContent.trim(); + document.getElementById('parsedParam_' + randomName).value = parseValue(data.substring(8, data.length - 8)); + document.getElementById("parsedVersion_" + randomName).value = hexToAscii(data.substring(0, 8)); + document.getElementById("crc32_" + randomName).value = data.substring(data.length - 8, data.length); + } + } +} + +function doSimplify() { + const rows = document.getElementById('parsedCompressedParams').rows; + const rows_params = document.getElementById('dataParams').rows; + for (let i = 0; i < rows.length; i++) { + if (rows[i].id) { + let randomName; + if (rows_params.length > 3) { + for (let j = 2; j < rows_params.length - 2; j++) { + if (rows_params[j].innerHTML.includes(document.getElementById('parsedDatasetName_' + rows[i].id).value)) { + randomName = rows_params[j].id; + break; + } + } + } + if (!randomName) { + randomName = addNewParam(); + } + document.getElementById('flashOffset_' + randomName).defaultValue = document.getElementById('parsedFlashOffset_' + rows[i].id).value; + document.getElementById('datasetName_' + randomName).defaultValue = document.getElementById('parsedDatasetName_' + rows[i].id).value; + document.getElementById("version_" + randomName).value = document.getElementById("parsedVersion_" + rows[i].id).value.substring(0, 2); + document.getElementById("param_" + randomName).value = document.getElementById('parsedParam_' + rows[i].id).value; + if (isLittleEndian(cleanHexValue(document.getElementById('parsedParam_' + rows[i].id).value))) { + document.getElementById('littleEndian_' + randomName).checked = true; + } + doCRC16Calc(randomName); + } + } + doClearParsedParams(); +} \ No newline at end of file diff --git a/docs/en/assets/js/fecGenerator.js b/docs/en/assets/js/fecGenerator.js new file mode 100644 index 000000000..543685bd9 --- /dev/null +++ b/docs/en/assets/js/fecGenerator.js @@ -0,0 +1,152 @@ +const TRAILER_HEX = "0000000000000000003B0FC769BFFF15ECD445B8196D2203D6D56BD8F22748B37D68F863DAC57E23C90A5FEEF0C06394C8D48A2EAA4F0FB658557400E66441DDC7D5AC3610AA4D45056C0C6E17C7E4B60C40E52FFA891938AF186ED20AE83A99EB10F3088479E6CBD2770C1563B5AE235B440BEF16EBE696576E108F2F9F897D963DEFBAD3ABDF2FFE"; + +let codes = []; +let selectedCount = 0; + +function $(selector) { + return document.querySelector(selector); +} + +function $all(selector) { + return document.querySelectorAll(selector); +} + +function validateLength(actual, expected, alertId, label) { + const el = document.getElementById(alertId); + if (!el) return; + + el.classList.remove('alert-success', 'alert-danger'); + + if (actual === 0) { + el.classList.add('alert-danger'); + el.innerHTML = `${label} input cannot be empty!`; + el.style.display = ''; + } else if (actual !== expected) { + el.classList.add('alert-danger'); + el.innerHTML = `${label} should be ${expected} characters long! ${actual}/${expected}`; + el.style.display = ''; + } else { + el.classList.add('alert-success'); + el.innerHTML = `${label} status: OK`; + el.style.display = ''; + } +} + +function stringToHex(str) { + return Array.from(str) + .map(ch => ch.charCodeAt(0).toString(16)) + .join(""); +} + +function hasCode(list, code) { + return list.some(item => item.code === code); +} + +function renderCodes() { + const codesContainer = document.getElementById("codes"); + const vinInput = document.getElementById("vin"); + const vcrnInput = document.getElementById("vcrn"); + const jumbotron = document.querySelector(".jumbotron"); + + if (!codesContainer || !vinInput || !vcrnInput) return; + + codesContainer.innerHTML = ""; + + const vin = vinInput.value; + const vcrn = vcrnInput.value; + + const timestampHex = Math.floor(Date.now() / 1000) + .toString(16) + .toUpperCase(); + + codes.forEach(item => { + if (!item.visible) return; + + const hexVin = stringToHex(vin).toUpperCase(); + const finalCode = `1102${item.code}03${vcrn.toUpperCase()}${hexVin}00${timestampHex}${TRAILER_HEX}`; + + const wrapper = document.createElement("div"); + wrapper.className = "form-group"; + + const label = document.createElement("label"); + label.className = "font-weight-bold"; + label.textContent = item.name; + + const textarea = document.createElement("textarea"); + textarea.className = "form-control h-100"; + textarea.readOnly = true; + textarea.value = finalCode; + + // Выделять текст при клике/фокусе + textarea.addEventListener("click", function () { + this.select(); + }); + textarea.addEventListener("focus", function () { + this.select(); + }); + + wrapper.appendChild(label); + wrapper.appendChild(textarea); + codesContainer.appendChild(wrapper); + }); + + if (jumbotron) { + jumbotron.style.display = selectedCount > 0 ? "none" : ""; + } +} + +const vinEl = document.getElementById("vin"); +if (vinEl) { + vinEl.addEventListener("keyup", function () { + const value = this.value; + codes.forEach(item => (item.vin = value)); + + validateLength(value.length, 17, "avin", "VIN"); + renderCodes(); + }); +} + +const vcrnEl = document.getElementById("vcrn"); +if (vcrnEl) { + vcrnEl.addEventListener("keyup", function () { + const value = this.value; + codes.forEach(item => (item.vcrn = value)); + + validateLength(value.length, 10, "avcrn", "VCRN"); + renderCodes(); + }); +} + +$all(".checkbox input").forEach(input => { + input.addEventListener("change", function () { + const codeValue = this.value; + const name = this.name; + + if (this.checked) { + if (hasCode(codes, codeValue)) { + codes.forEach(item => { + if (item.code === codeValue) item.visible = true; + }); + } else { + const vinVal = vinEl ? vinEl.value : ""; + const vcrnVal = vcrnEl ? vcrnEl.value : ""; + + codes.push({ + code: codeValue, + vcrn: vcrnVal, + vin: vinVal, + visible: true, + name: name + }); + } + selectedCount++; + } else { + codes.forEach(item => { + if (item.code === codeValue) item.visible = false; + }); + selectedCount--; + } + + renderCodes(); + }); +}); diff --git a/docs/en/assets/js/longCoding.js b/docs/en/assets/js/longCoding.js new file mode 100644 index 000000000..f462c1810 --- /dev/null +++ b/docs/en/assets/js/longCoding.js @@ -0,0 +1,616 @@ +"use strict"; + +let curSelectedByte = -1; +let defaultByteSize = 30; +let labelsFromFile = null; + +// ========== Additional methods ========== + +function cleanHexValue(str) { + if (!str) return ""; + return str.toUpperCase().replace(/[^0-9A-F]/g, ""); +} + +function trim(str) { + return (str || "").replace(/^\s*|\s*$/g, ""); +} + +function calculateByteIdFromNumber(num) { + return num < 10 ? "0" + num : String(num); +} + +function from10toradix(value, radix) { + const ConvArray = [ + "0","1","2","3","4","5","6","7","8","9", + "A","B","C","D","E","F" + ]; + + let intnum = parseInt(value, 10); + if (Number.isNaN(intnum)) { + return "NaN"; + } + + if (intnum === 0) { + return "0"; + } + + let retval = ""; + let i = 0; + + while (intnum > 0) { + i++; + const digit = intnum % radix; + retval = ConvArray[digit] + retval; + intnum = Math.floor(intnum / radix); + + if (i > 100) { + return "NaN"; // + } + } + + return retval || "0"; +} + +function hexToBin(value) { + const dezVal = parseInt(value, 16); + const bitValueBin = from10toradix(dezVal, 2); + return addLeadingZeros(bitValueBin); +} + +function addLeadingZeros(binVal) { + let retString = binVal || ""; + const anz = 8 - retString.length; + for (let i = 0; i < anz; i++) { + retString = "0" + retString; + } + return retString; +} + +function activateFields() { + const loading = document.getElementById("loading"); + const bitDescription = document.getElementById("bitDescription"); + if (loading) loading.style.display = "none"; + if (bitDescription) bitDescription.style.display = "block"; +} + +// ========== Initialisation ========== + +function init() { + const urlParams = new URLSearchParams(window.location.search); + + createByteDescription(defaultByteSize); + createBitDescription(); + wireBitEvents(); + wireByteEvents(); + + const codeParam = urlParams.get("code"); + const labelParam = urlParams.get("label"); + + if (labelParam != null) { + const langSel = document.getElementById("langBits"); + if (langSel) langSel.value = labelParam; + } + if (codeParam != null) { + const orig = document.getElementById("origCode"); + if (orig) { + orig.value = codeParam; + checkOrig(); + } + } +} + +// ========== Main functions ========== + +function checkOrig() { + const filter = /^[A-F0-9]+$/; + const origEl = document.getElementById("origCode"); + if (!origEl) return false; + + let orig = origEl.value; + orig = cleanHexValue(orig); + origEl.value = orig; + + const bitDescription = document.getElementById("bitDescription"); + if (bitDescription) bitDescription.style.display = "none"; + + if (orig.length % 2 === 0 && orig.length > 0) { + if (filter.test(orig)) { + const loading = document.getElementById("loading"); + if (loading) loading.style.display = "inline-block"; + + fillBytes(); + + const langBits = document.getElementById("langBits"); + if (langBits && langBits.value !== "none") { + fetch("../../labels/" + langBits.value + "_labels.txt") + .then(response => response.text()) + .then(text => { + labelsFromFile = text.split("\n"); + }) + .then(() => { + selByte(0); + }) + .catch(() => { + labelsFromFile = null; + selByte(0); + }); + } else { + selByte(curSelectedByte >= 0 ? curSelectedByte : 0); + } + return true; + } else { + alert( + "Кодировка содержит неверные символы. Поддерживаются только латинские буквы A-F и цифры 0-9. " + + "Coding has wrong symbols. Latin characters A-F and digits 0-9 are allowed." + ); + clearFields(); + } + } else { + if (orig.length > 0) { + alert( + "Неверное значение. Кодировка должна содержать чётное количество символов. " + + "Incorrect value. Coding should have even number of symbols." + ); + } + clearFields(); + } + return false; +} + +function fillBytes() { + const origEl = document.getElementById("origCode"); + const calcEl = document.getElementById("calcCode"); + if (!origEl || !calcEl) return; + + const orig = origEl.value; + const newNum = orig.toString().match(/.{1,2}/g) || []; + + if (newNum.length > defaultByteSize) { + defaultByteSize = newNum.length; + createByteDescription(defaultByteSize); + wireByteEvents(); + } + + for (let i = 0; i < newNum.length; i++) { + const el = document.getElementById("byte" + calculateByteIdFromNumber(i)); + if (el) el.value = newNum[i]; + } + + calcEl.value = orig; + curSelectedByte = 0; +} + +function fillDezAndBits(bin) { + for (let i = 0; i < 8; i++) { + const bit = document.getElementById("bit" + i); + if (bit) bit.value = bin.substr(7 - i, 1); + } +} + +function comparingOfBits(bin1, bin2) { + if (bin1 === bin2) return true; + + const bin1a = bin1.split("").map(Number); + const bin2a = bin2.split("").map(Number); + + let equal = false; + for (let i = 0; i < bin2a.length; i++) { + if (bin2a[i] === 1) { + if (bin2a[i] === bin1a[i]) { + equal = true; + } else { + return false; + } + } + } + return equal; +} + +function calculateBitValue(binOld, binNew) { + if (binNew === binOld) return binNew; + + const binNewAr = binNew.split("").map(Number); + const binOldAr = binOld.split("").map(Number); + + for (let i = 0; i < binNewAr.length; i++) { + if (binNewAr[i] !== binOldAr[i]) { + const idx = 7 - i; + const cb = document.getElementById("setModifiedBits" + idx); + if (cb) cb.checked = binNewAr[i] === 1; + chgBit(idx); + } + } +} + +function fillBitCheckBoxes(bin, reduced) { + for (let i = 0; i < 8; i++) { + const cb = document.getElementById("setModifiedBits" + (7 - i)); + if (cb) cb.checked = bin.substr(i, 1) === "1"; + } + + const bitVariants = document.getElementById("bitVariants"); + if (bitVariants) bitVariants.innerHTML = ""; + + for (let j = 0; j < 8; j++) { + const label = document.getElementById("setModifiedBitsLabel" + j); + if (label) label.innerHTML = " Bit " + j; + } + + if (reduced && reduced.length) { + reduced.forEach(label => { + if (!label) return; + + if (!isNaN(label.bit)) { + const lab = document.getElementById("setModifiedBitsLabel" + label.bit); + if (lab) lab.innerHTML = " " + label.description; + } else { + if (!bitVariants) return; + + const wrapper = document.createElement("div"); + const text = document.createElement("span"); + text.textContent = label.description + " (bits " + label.bit + "): "; + + const selectElement = document.createElement("select"); + selectElement.id = "select" + label.bit; + + const variants = (label.variants || "").split(";"); + variants.forEach(vStr => { + const cleaned = trim(vStr); + if (!cleaned) return; + + const parts = cleaned.split("-"); + const hex = trim(parts[0] || ""); + const desc = trim(parts[1] || cleaned); + + const currentOption = comparingOfBits(bin, hexToBin(hex)); + + const opt = document.createElement("option"); + opt.value = hex; + opt.textContent = desc; + if (currentOption) opt.selected = true; + selectElement.appendChild(opt); + }); + + let previous = selectElement.value; + + selectElement.addEventListener("focus", e => { + previous = e.target.value; + }); + + selectElement.addEventListener("change", e => { + const newVal = e.target.value; + if (previous !== newVal) { + calculateBitValue(hexToBin(previous), hexToBin(newVal)); + previous = newVal; + } + }); + + wrapper.appendChild(text); + wrapper.appendChild(selectElement); + wrapper.appendChild(document.createElement("br")); + wrapper.appendChild(document.createElement("br")); + bitVariants.appendChild(wrapper); + } + }); + } + + activateFields(); +} + +function selByte(noByte) { + const byteLabel = document.getElementById("Byte"); + if (byteLabel) byteLabel.innerHTML = "(Byte " + noByte + ")"; + + curSelectedByte = noByte; + + const calcCode = document.getElementById("calcCode"); + if (!calcCode) return; + + const startPos = noByte * 2; + const bitValueHex = calcCode.value.substr(startPos, 2); + + if (bitValueHex === "") { + clearBits(); + for (let i = 0; i < 8; i++) { + const bit = document.getElementById("bit" + i); + if (bit) bit.value = "0"; + } + toggleSelectedByteColor(noByte); + return; + } + + const fullBitString = hexToBin(bitValueHex); + + let reduced = null; + if (labelsFromFile != null) { + reduced = labelsFromFile.reduce((filtered, option) => { + const opt = option.split(","); + if (opt[0] == noByte) { + const labelValue = { + byte: opt[0], + bit: opt[1], + description: opt[2], + variants: opt[3] + }; + filtered.push(labelValue); + } + return filtered; + }, []); + } + + fillDezAndBits(fullBitString); + fillBitCheckBoxes(fullBitString, reduced); + toggleSelectedByteColor(noByte); +} + +function chgBit(no) { + const cb = document.getElementById("setModifiedBits" + no); + const bit = document.getElementById("bit" + no); + if (!cb || !bit) return; + + bit.value = cb.checked ? "1" : "0"; + setBits(); +} + +function setBits() { + let tmpBitStr = ""; + for (let i = 7; i >= 0; i--) { + const bit = document.getElementById("bit" + i); + tmpBitStr += bit ? bit.value : "0"; + } + + if (!tmpBitStr) return false; + + const tmpVal = parseInt(tmpBitStr, 2); + let hexValue = from10toradix(tmpVal, 16); + + if (hexValue.length === 1) hexValue = "0" + hexValue; + else if (hexValue.length < 1) hexValue = "00"; + + resetByteHex(hexValue); + selByte(curSelectedByte); + return true; +} + +function resetCalcCode() { + let tmpVal = ""; + for (let i = 0; i < defaultByteSize; i++) { + const el = document.getElementById("byte" + calculateByteIdFromNumber(i)); + if (el) tmpVal += el.value; + } + const calcCode = document.getElementById("calcCode"); + if (calcCode) calcCode.value = trim(tmpVal); +} + +function resetByteHex(hexVal) { + const el = document.getElementById("byte" + calculateByteIdFromNumber(curSelectedByte)); + if (el) el.value = hexVal; + resetCalcCode(); +} + +function clearBits() { + for (let i = 0; i < 8; i++) { + const bit = document.getElementById("bit" + i); + if (bit) bit.value = ""; + } + for (let j = 0; j < 8; j++) { + const cb = document.getElementById("setModifiedBits" + j); + if (cb) cb.checked = false; + const label = document.getElementById("setModifiedBitsLabel" + j); + if (label) label.innerHTML = " Bit " + j; + } + const bitVariants = document.getElementById("bitVariants"); + if (bitVariants) bitVariants.innerHTML = ""; +} + +function clearFields() { + curSelectedByte = -1; + const byteLabel = document.getElementById("Byte"); + if (byteLabel) byteLabel.innerHTML = ""; + + for (let i = 0; i < defaultByteSize; i++) { + const el = document.getElementById("byte" + calculateByteIdFromNumber(i)); + if (el) el.value = ""; + } + + clearBits(); + + const calcCode = document.getElementById("calcCode"); + if (calcCode) calcCode.value = ""; + + defaultByteSize = 30; + createByteDescription(defaultByteSize); + wireByteEvents(); +} + +function clearAll() { + labelsFromFile = null; + clearFields(); + + const orig = document.getElementById("origCode"); + if (orig) orig.value = ""; + + const bitDescription = document.getElementById("bitDescription"); + if (bitDescription) bitDescription.style.display = "none"; + + const langBits = document.getElementById("langBits"); + if (langBits) langBits.value = "none"; + + const loading = document.getElementById("loading"); + if (loading) loading.style.display = "none"; +} + +function clearLabels() { + labelsFromFile = null; + clearBits(); + if (curSelectedByte >= 0) { + selByte(curSelectedByte); + } +} + +function toggleSelectedByteColor(noByte) { + for (let i = 0; i < defaultByteSize; i++) { + const el = document.getElementById("byte" + calculateByteIdFromNumber(i)); + if (!el) continue; + el.className = i === noByte ? "byteSelected" : "byteNotSelected"; + } +} + +function selectAll(ctrl) { + if (ctrl && typeof ctrl.select === "function") { + ctrl.select(); + } +} + +function isBinary(event) { + const charCode = event.which ? event.which : event.keyCode; + + const controlKeys = [8, 9, 13, 35, 36, 37, 39, 46]; + if (controlKeys.includes(charCode)) { + if (charCode === 13) setBits(); + return true; + } + + if (charCode === 48 || charCode === 49) { + return true; + } + + event.preventDefault(); + return false; +} + +// ========== Generation of html tables ========== + +function createByteDescription(countOfBytes) { + const container = document.getElementById("bytesDescription"); + if (!container) return; + + const bytesPerLine = 30; + const colsFirstLine = Math.min(countOfBytes, bytesPerLine); + const lines = Math.ceil(countOfBytes / bytesPerLine); + + let html = ""; + html += ""; + + for (let line = 0; line < lines; line++) { + const start = line * bytesPerLine; + const end = Math.min(start + bytesPerLine, countOfBytes); + + html += ""; + for (let i = start; i < end; i++) { + html += ""; + } + html += ""; + for (let j = start; j < end; j++) { + const byteName = calculateByteIdFromNumber(j); + html += ""; + } + html += ""; + } + + html += "
Байты / Bytes
" + calculateByteIdFromNumber(i) + "
"; + html += ""; + html += "
"; + container.innerHTML = html; +} + +function createBitDescription() { + const container = document.getElementById("bitDescription"); + if (!container) return; + + let html = ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += ""; + html += "
"; + html += "Bits
"; + html += "
"; + html += ""; + html += ""; + + for (let i = 7; i >= 0; i--) { + html += ""; + } + + html += ""; + + for (let j = 7; j >= 0; j--) { + html += ""; + } + + html += "
" + i + "
"; + + for (let k = 0; k < 8; k++) { + html += ""; + html += "
"; + } + + html += "
"; + + container.innerHTML = html; +} + +// ========== Events handling ========== + +function wireBitEvents() { + for (let i = 0; i < 8; i++) { + const bit = document.getElementById("bit" + i); + if (!bit) continue; + + bit.onkeydown = e => isBinary(e); + bit.onclick = () => selectAll(bit); + bit.onblur = () => setBits(); + } + + for (let j = 0; j < 8; j++) { + const cb = document.getElementById("setModifiedBits" + j); + if (!cb) continue; + cb.onclick = () => chgBit(j); + } +} + +function wireByteEvents() { + const container = document.getElementById("bytesDescription"); + if (!container) return; + + container.addEventListener("click", e => { + const target = e.target; + if (!target || target.tagName !== "INPUT") return; + if (!target.id || !target.id.startsWith("byte")) return; + + const index = parseInt(target.getAttribute("data-index"), 10); + if (Number.isNaN(index)) return; + + selByte(index); + selectAll(target); + }); + + container.addEventListener("change", e => { + const target = e.target; + if (!target || target.tagName !== "INPUT") return; + if (!target.id || !target.id.startsWith("byte")) return; + + const index = parseInt(target.getAttribute("data-index"), 10); + if (Number.isNaN(index)) return; + + setByte(target, index); + }); +} + +function setByte(ctrl, byteIndex) { + const regexp = /^[0-9a-fA-F]*$/; + if (!regexp.test(ctrl.value)) { + alert(ctrl.value + " - неверный формат введенного значения! Incorrect value!"); + ctrl.value = "00"; + } else { + ctrl.value = ctrl.value.toUpperCase(); + } + resetCalcCode(); + selByte(byteIndex); +} \ No newline at end of file diff --git a/docs/en/assets/js/tiresGenerator.js b/docs/en/assets/js/tiresGenerator.js new file mode 100644 index 000000000..663932d02 --- /dev/null +++ b/docs/en/assets/js/tiresGenerator.js @@ -0,0 +1,524 @@ +"use strict"; + +function addToDataset(dataset, info) { + dataset.value += info; +} + +function generateHeader(dataset, docType, ecuName, ecuOffset) { + if (docType === "vcp") { + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "20103\n"); + addToDataset(dataset, "24\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "" + ecuName + "\n"); + addToDataset(dataset, "DFN_HEX\n"); + addToDataset(dataset, "" + ecuOffset + "\n"); + addToDataset(dataset, "0x0800\n"); + addToDataset(dataset, ""); + } else if (docType === "odis") { + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset( + dataset, + "" + ); + } else { + addToDataset(dataset, "\n\nERROR! Unknown docType (" + docType + ")\n\n"); + } +} + +function generateFooter(dataset, docType) { + if (docType === "vcp") { + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + } else if (docType === "odis") { + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + addToDataset(dataset, "\n"); + } else { + addToDataset(dataset, "\n\nERROR! Unknown docType\n\n"); + } +} + +function appendType(byteStream, ecuType) { + const type = parseInt(ecuType, 16); + byteStream.push(Number.isNaN(type) ? 0x00 : type); +} + +function appendSetName(byteStream, setName) { + let name = String(setName || ""); + let length = name.length; + + if (length > 60) { + name = name.substring(0, 60); + length = name.length; + } + + byteStream.push(length); + + for (let i = 0; i < length; i++) { + byteStream.push(name.charCodeAt(i)); + } + + for (let i = length; i < 61; i++) { + byteStream.push(0x00); + } +} + +function calcPressureValue(pressureStr) { + let trimmed = (pressureStr || "").trim(); + if (trimmed.length === 0) return 0xFF; + + trimmed = trimmed.replace(",", "."); + const val = parseFloat(trimmed); + + if (Number.isNaN(val)) return 0xFF; + + let scaled = Math.round(val * 10); + if (scaled < 0) scaled = 0; + if (scaled > 0xFF) scaled = 0xFF; + return scaled; +} + +function appendSetPressure(byteStream, ecuName, pressureValues) { + if (ecuName === "3AA907273H") { + byteStream.push(calcPressureValue(pressureValues.frontPartial)); + byteStream.push(calcPressureValue(pressureValues.frontFull)); + byteStream.push(calcPressureValue(pressureValues.frontComfort)); + byteStream.push(calcPressureValue(pressureValues.rearPartial)); + byteStream.push(calcPressureValue(pressureValues.rearFull)); + byteStream.push(calcPressureValue(pressureValues.rearComfort)); + } else { + byteStream.push(calcPressureValue(pressureValues.frontFull)); + byteStream.push(calcPressureValue(pressureValues.frontPartial)); + byteStream.push(calcPressureValue(pressureValues.frontComfort)); + byteStream.push(calcPressureValue(pressureValues.rearFull)); + byteStream.push(calcPressureValue(pressureValues.rearPartial)); + byteStream.push(calcPressureValue(pressureValues.rearComfort)); + } +} + +function appendTireSet(byteStream, ecuName, tireSet) { + for (let i = 0; i < 61; i++) { + byteStream.push(0x00); + } + + appendSetName(byteStream, tireSet.name); + appendSetPressure(byteStream, ecuName, tireSet); + + for (let i = 0; i < 6; i++) { + byteStream.push(0x00); + } +} + +function appendVersion(byteStream, ecuVersion) { + const v = String(ecuVersion || ""); + const version1 = v.charCodeAt(0) || 0x00; + const version2 = v.charCodeAt(1) || 0x00; + + byteStream.push(version1); + byteStream.push(version2); +} + +function appendCRC(byteStream) { + const lookupTable = []; + + for (let i = 0; i < 256; i++) { + let c = i; + + for (let k = 0; k < 8; k++) { + c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1); + } + + lookupTable[i] = c; + } + + let crc = 0xFFFFFFFF; + + for (let i = 0; i < byteStream.length; i++) { + crc = (crc >>> 8) ^ lookupTable[(crc ^ byteStream[i]) & 0xFF]; + } + + crc = crc ^ 0xFFFFFFFF; + + byteStream.push((crc >>> 24) & 0xFF); + byteStream.push((crc >>> 16) & 0xFF); + byteStream.push((crc >>> 8) & 0xFF); + byteStream.push(crc & 0xFF); +} + +function generateDataset(ecuName, ecuType, ecuVersion, tireSets) { + const byteStream = []; + + appendType(byteStream, ecuType); + byteStream.push(0x00); + + for (let i = 0; i < 11; i++) { + appendTireSet(byteStream, ecuName, tireSets[i]); + } + + for (let i = 0; i < 555; i++) { + byteStream.push(0xFF); + } + + appendVersion(byteStream, ecuVersion); + appendCRC(byteStream); + + return byteStream; +} + +function generateBody(dataset, ecuName, ecuType, ecuVersion, tireSets) { + const byteStream = generateDataset(ecuName, ecuType, ecuVersion, tireSets); + + for (let i = 0; i < byteStream.length; i++) { + let str = byteStream[i].toString(16).toUpperCase(); + + if (str.length < 2) { + str = "0" + str; + } + + if (i > 0) { + addToDataset(dataset, ",0x" + str); + } else { + addToDataset(dataset, "0x" + str); + } + } +} + +function doGenerate() { + const numTireSetsRaw = document.getElementById("numTireSets").value; + let numTireSets = parseInt(numTireSetsRaw, 10); + if (Number.isNaN(numTireSets)) numTireSets = 0; + + if (numTireSets < 1) { + window.alert("Количество комплектов шин должно быть больше 0.\nNumber of tire sets should be greater than 0."); + return; + } + + const dataset = document.getElementById("dataset"); + dataset.value = ""; + + const docType = document.getElementById("docType").value; + const ecuObj = document.getElementById("ecuModel"); + const ecuOptions = ecuObj.options[ecuObj.selectedIndex]; + const ecuName = ecuOptions.text; + const ecuOffset = ecuOptions.getAttribute("ecuOffset"); + const ecuVersion = ecuOptions.getAttribute("ecuVersion"); + const ecuType = ecuOptions.getAttribute("ecuType"); + + const tireSets = []; + + for (let index = 1; index <= 11; index++) { + const table = document.getElementById("t" + index); + const isHidden = table.hasAttribute("hidden"); + + if (isHidden) { + tireSets.push({ + name: "", + frontFull: "", + rearFull: "", + frontPartial: "", + rearPartial: "", + frontComfort: "", + rearComfort: "" + }); + } else { + const nameValue = document.getElementById("t" + index + "name").value; + + if (!/^[a-zA-Z]+$/.test(nameValue)) { + window.alert( + "Имя конфигурации #" + index + " может содержать только латинские буквы.\n" + + "Configuration name should have only latin symbols." + ); + return; + } + + const comfortHidden = document.getElementById("trcomfort" + index).hasAttribute("hidden"); + + const set = { + name: nameValue, + frontFull: document.getElementById("t" + index + "pff").value, + rearFull: document.getElementById("t" + index + "prf").value, + frontPartial: document.getElementById("t" + index + "pfp").value, + rearPartial: document.getElementById("t" + index + "prp").value, + frontComfort: comfortHidden ? "" : document.getElementById("t" + index + "pfc").value, + rearComfort: comfortHidden ? "" : document.getElementById("t" + index + "prc").value + }; + + tireSets.push(set); + } + } + + if (docType === "binary") { + const byteStream = generateDataset(ecuName, ecuType, ecuVersion, tireSets); + + for (let i = 0; i < byteStream.length; i++) { + if ((i % 16) === 0) { + if (i > 0) addToDataset(dataset, "\n"); + + let offset = i.toString(16).toUpperCase(); + while (offset.length < 4) offset = "0" + offset; + addToDataset(dataset, offset + " "); + } + + let value = byteStream[i].toString(16).toUpperCase(); + if (value.length < 2) value = "0" + value; + + addToDataset(dataset, value + " "); + } + + return new Uint8Array(byteStream); + } else { + generateHeader(dataset, docType, ecuName, ecuOffset); + generateBody(dataset, ecuName, ecuType, ecuVersion, tireSets); + generateFooter(dataset, docType); + + const text = dataset.value; + const byteStreamXml = new Uint8Array(text.length); + + for (let i = 0; i < text.length; i++) { + byteStreamXml[i] = text.charCodeAt(i); + } + + return byteStreamXml; + } +} + +function doGenerateDl() { + const content = doGenerate(); + if (!content) return; + + const blob = new Blob([content], { type: "application/octet-stream" }); + const element = window.document.createElement("a"); + + if ("download" in element) { + const docType = document.getElementById("docType").value; + const ecuObj = document.getElementById("ecuModel"); + const ecuOptions = ecuObj.options[ecuObj.selectedIndex]; + const ecuName = ecuOptions.text; + const ext = (docType === "binary") ? ".bin" : ".xml"; + + element.href = window.URL.createObjectURL(blob); + element.download = ecuName + ext; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + } else { + const fileReader = new FileReader(); + fileReader.onload = function () { + location.href = this.result; + }; + fileReader.readAsDataURL(blob); + } +} + +function generatePD(id) { + let html = ""; + + html += ""; + + return html; +} + +function updateTireSets() { + const numTireSetsRaw = document.getElementById("numTireSets").value; + let numTireSets = parseInt(numTireSetsRaw, 10); + if (Number.isNaN(numTireSets)) numTireSets = 0; + + const enableIndividual = document.getElementById("enableIndividual").checked; + const enableComfort = document.getElementById("enableComfort").checked; + + for (let index = 1; index <= 11; index++) { + const table = document.getElementById("t" + index); + const br1 = document.getElementById("br1" + index); + const trcomfort = document.getElementById("trcomfort" + index); + + const shouldShowThisSet = + ((index === 11) && enableIndividual) || + (index <= numTireSets); + + if (shouldShowThisSet) { + table.removeAttribute("hidden"); + br1.removeAttribute("hidden"); + } else { + table.setAttribute("hidden", ""); + br1.setAttribute("hidden", ""); + } + + if (enableComfort) { + trcomfort.removeAttribute("hidden"); + } else { + trcomfort.setAttribute("hidden", ""); + } + } +} + +function createTireSets() { + let html = ""; + + for (let index = 1; index <= 11; index++) { + html += " "; + html += " "; + + if (index === 11) { + html += " "; + } else { + html += " "; + } + + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += " "; + html += "
Индивидуальная настройка давления в шинах / Tire set IndividualНастройка давления в шинах / Tire set: #" + index + "
Имя
Тип загрузки машины / Load situationПередние колеса / Front wheelsЗадние колеса / Rear wheels
Полная загрузка / Full load" + generatePD("t" + index + "pff") + "" + generatePD("t" + index + "prf") + "
Стандартная загрузка / Standard load" + generatePD("t" + index + "pfp") + "" + generatePD("t" + index + "prp") + "
Комфортная загрузка / Comfort load" + generatePD("t" + index + "pfc") + "" + generatePD("t" + index + "prc") + "
"; + html += "
"; + } + + document.getElementById("tireSetList").innerHTML = html; + updateTireSets(); +} + +let configVersion = "1.0"; + +const configFields = [ + "docType", "ecuModel", "numTireSets", "@enableIndividual", "@enableComfort", + "t1name", "t1pff", "t1prf", "t1pfp", "t1prp", "t1pfc", "t1prc", + "t2name", "t2pff", "t2prf", "t2pfp", "t2prp", "t2pfc", "t2prc", + "t3name", "t3pff", "t3prf", "t3pfp", "t3prp", "t3pfc", "t3prc", + "t4name", "t4pff", "t4prf", "t4pfp", "t4prp", "t4pfc", "t4prc", + "t5name", "t5pff", "t5prf", "t5pfp", "t5prp", "t5pfc", "t5prc", + "t6name", "t6pff", "t6prf", "t6pfp", "t6prp", "t6pfc", "t6prc", + "t7name", "t7pff", "t7prf", "t7pfp", "t7prp", "t7pfc", "t7prc", + "t8name", "t8pff", "t8prf", "t8pfp", "t8prp", "t8pfc", "t8prc", + "t9name", "t9pff", "t9prf", "t9pfp", "t9prp", "t9pfc", "t9prc", + "t10name", "t10pff", "t10prf", "t10pfp", "t10prp", "t10pfc", "t10prc", + "t11name", "t11pff", "t11prf", "t11pfp", "t11prp", "t11pfc", "t11prc" +]; + +function loadConfig() { + const result = window.prompt( + "Восстановление конфигурации. Load configuration\n\n" + + "Пожалуйста введите код, созданный ранее в данном приложении.\n" + + "Please enter configuration code, created earlier with this tool", + "" + ); + + if (result === null) return; + + let code; + try { + code = window.atob(result); + } catch (e) { + window.alert("Введён некорректный код конфигурации (не base64).\nInvalid configuration code."); + return; + } + + const keyValue = code.split("&"); + + for (let i = 0; i < keyValue.length; i++) { + const data = keyValue[i].split("="); + + if (data.length !== 2) continue; + + if (data[0] === "configVersion") { + if (data[1] !== configVersion) { + window.alert( + "Введенный код конфигурации не может может быть восстановлен\n" + + "Configuration was created using an incompatible version of this tool and can not be restored\n\n" + + "Created version: " + data[1] + "\nCurrent version: " + configVersion + ); + return; + } + } else if (data[0].charAt(0) === "@") { + const idCheck = data[0].substr(1); + const elCheck = document.getElementById(idCheck); + if (elCheck) { + elCheck.checked = (data[1] === "true"); + } + } else { + const el = document.getElementById(data[0]); + if (el) { + el.value = data[1]; + } + } + } + + updateTireSets(); +} + +function saveConfig() { + let code = "configVersion=" + configVersion + "&"; + + for (let i = 0; i < configFields.length; i++) { + const fieldName = configFields[i]; + + if (fieldName.charAt(0) === "@") { + const idCheck = fieldName.substr(1); + const elCheck = document.getElementById(idCheck); + code += fieldName + "=" + (elCheck ? elCheck.checked : false) + "&"; + } else { + const el = document.getElementById(fieldName); + code += fieldName + "=" + (el ? el.value : "") + "&"; + } + } + + const encoded = window.btoa(code); + + window.prompt( + "Сохранить настройки. Save configuration\n\n" + + "Пожалуйста, сохраните код, приведённый ниже. Он может быть использован для восстановления настроек в любое время.\n" + + "Please backup the configuration code below, it can be used to restore the current configuration at a later point in time.", + encoded + ); +} + +window.onload = function () { + createTireSets(); +}; diff --git a/docs/en/assets/js/xor.js b/docs/en/assets/js/xor.js new file mode 100644 index 000000000..6301ccfc0 --- /dev/null +++ b/docs/en/assets/js/xor.js @@ -0,0 +1,44 @@ +let decodeString = "c9d2"; +let codeLength = 4; + +function init() { + const urlParams = new URLSearchParams(window.location.search); + const codeParam = urlParams.get('code'); + + if (codeParam) { + document.getElementById("origCode").value = codeParam; + calculateXor(); + } +} + +function calculateXor() { + let input = document.getElementById("origCode").value.trim(); + let result = ''; + + if (input.length !== codeLength) { + alert( + "Длина вводимого значения должна быть " + codeLength + " символа.\n" + + "Input HEX value must be exactly " + codeLength + " characters long." + ); + return; + } + + if (!/^[0-9a-fA-F]+$/.test(input)) { + alert("Допустимы только HEX символы (0-9, A-F).\nOnly HEX characters (0-9, A-F) are allowed."); + return; + } + + for (let index = 0; index < codeLength; index++) { + const a = parseInt(input.charAt(index), 16); + const b = parseInt(decodeString.charAt(index), 16); + const temp = (a ^ b).toString(16).toUpperCase(); + result += temp; + } + + document.getElementById("calcCode").value = result; +} + +function clearAll() { + document.getElementById("origCode").value = ""; + document.getElementById("calcCode").value = ""; +} diff --git a/docs/history.md b/docs/history.md index 7b69fc628..65c390aae 100644 --- a/docs/history.md +++ b/docs/history.md @@ -3,6 +3,9 @@ comments: false --- # История изменений +### 03.01.2026 +1. Исправлена ошибка при загрузке js скриптов + ### 10.12.2025 1. Обновлен UI 2. Добавлены новые кодировки для ряда платформ diff --git a/mkdocs.yml b/mkdocs.yml index e978c6206..004edaea3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -14,7 +14,7 @@ repo_url: https://github.com/Kanaduchi/vwcoding edit_uri: https://github.com/Kanaduchi/vwcoding/edit/master/docs/ # Copyright -copyright: Copyright © 2025 - vwcoding.ru (info@vwcoding.ru) +copyright: Copyright © 2026 - vwcoding.ru (info@vwcoding.ru) # Main directory docs_dir: docs/ diff --git a/requirements.txt b/requirements.txt index 61e614be4..d8c16f9e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -mkdocs-material==9.7.0 +mkdocs-material==9.7.1 mkdocs-print-site-plugin==2.8 mkdocs-static-i18n==1.3.0 mkdocs-git-revision-date-localized-plugin==1.5.0 \ No newline at end of file