forked from M-Labs/web2019
Compare commits
17 Commits
1123794b1a
...
937e5768f4
Author | SHA1 | Date | |
---|---|---|---|
937e5768f4 | |||
b00ff0d403 | |||
33a6a55369 | |||
7b7faab505 | |||
2b1d56ef02 | |||
78a0111440 | |||
ed14964bb8 | |||
b32797039b | |||
6e8ba9d2ee | |||
cbf49b7712 | |||
769ce960d4 | |||
342742789d | |||
effcd917a0 | |||
d43ced5969 | |||
9896752e16 | |||
e79d33e2e5 | |||
53863b048c |
@ -88,6 +88,10 @@ We accept all major currencies including USD, EUR, RMB, GBP, BTC and XMR.
|
||||
|
||||
Yes, however processing credit cards is expensive and you need to cover the costs. 4\% card processing fee applies for payment by credit card in Hong Kong dollars. 7\% card processing and exchange fee applies for payment by credit card in US dollars.
|
||||
|
||||
##### I have overpaid or double-paid an invoice. What should I do?
|
||||
|
||||
Contact sb@m-labs.hk and we will return the excess funds within a few business days. You shall pay all banking and credit card charges incurred.
|
||||
|
||||
##### We are a distributor. Can we have exclusive rights to your products?
|
||||
|
||||
Exclusivity is subject to contractual minimum order volumes determined at our discretion, but generally exceeding USD 300,000.00 per year for most countries.
|
||||
|
@ -6,6 +6,8 @@ template = "page.html"
|
||||
|
||||
<div class="row d-flex align-items-center mt-5 mb-5">
|
||||
|
||||
##### Hong Kong
|
||||
|
||||
{% layout_div(css="col-12 col-md-6") %}
|
||||
|
||||
**M-Labs Limited**
|
||||
@ -30,6 +32,16 @@ Bus: Kodak House / North Point Government Office / North Point Fire Station / He
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row d-flex align-items-center mt-5 mb-5">
|
||||
|
||||
##### Philippines
|
||||
|
||||
**M-Labs Scientific Instruments OPC**
|
||||
|
||||
Unit 2502 Cityland 10 Tower 1, 154 H.V Dela Costa St., Makati City, Metro Manila, Philippines
|
||||
|
||||
</div>
|
||||
|
||||
<img class="img-fluid" src="/images/kodakview2.jpg" height="250" alt="view">
|
||||
|
||||
<small>Photo: Mateusz Wójcik</small>
|
||||
<small>View from the Hong Kong office (photo: Mateusz Wójcik)</small>
|
||||
|
@ -1,3 +1,11 @@
|
||||
- title: "Distributed quantum computing across an optical network link"
|
||||
authors: "D. Main, P. Drmota, D. P. Nadlinger, E. M. Ainley, A. Agrawal, B. C. Nichol, R. Srinivas, G. Araneda & D. M. Lucas"
|
||||
links:
|
||||
- name: "Nature (2025)"
|
||||
path: "https://www.nature.com/articles/s41586-024-08404-x"
|
||||
- name: "Announcement"
|
||||
path: "https://www.ox.ac.uk/news/2025-02-06-first-distributed-quantum-algorithm-brings-quantum-supercomputers-closer"
|
||||
|
||||
- title: "Fast quantum logic gates with trapped-ion qubits"
|
||||
authors: "V. M. Schäfer, C. J. Ballance, K. Thirumalai, L. J. Stephenson, T. G. Ballance, A. M. Steane & D. M. Lucas"
|
||||
links:
|
||||
|
@ -139,6 +139,17 @@ We welcome inquiries from research groups of all sizes.<br>[See what has been fu
|
||||
<a href="https://github.com/OxfordIonTrapGroup/ndscan" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="DAX - Duke ARTIQ extensions", sameheight=120) %}
|
||||
<small>A library to provide tools for system organization/abstraction and to improve usability by automating common functionality.</small>
|
||||
|
||||
<a href="https://gitlab.com/duke-artiq" target="_blank" rel="noopener noreferrer" itemprop="url">Repositories</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="flake8-artiq", sameheight=120) %}
|
||||
<small>A Flake8 plugin for checking ARTIQ code</small>
|
||||
|
||||
<a href="https://gitlab.com/duke-artiq/flake8-artiq" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="Oxford routines", sameheight=120) %}
|
||||
<small>Oxford Ion-Trap Group routines</small>
|
||||
@ -146,6 +157,12 @@ We welcome inquiries from research groups of all sizes.<br>[See what has been fu
|
||||
<a href="https://github.com/OxfordIonTrapGroup/oitg" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="ATOMIQ", sameheight=120) %}
|
||||
<small>An abstraction layer to move hardware specific information into a configuration layer and enables working with generic software objects representing the actual hardware in the lab.</small>
|
||||
|
||||
<a href="https://thequantumlaend.de/2024/03/07/atomiq-our-convenience-layer-for-artiq-now-available/" target="_blank" rel="noopener noreferrer" itemprop="url">Announcement</a> | <a href="https://gitlab.com/atomiq-project/atomiq" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="UCLA routines", sameheight=120) %}
|
||||
<small>ARTIQ experiments in use at UCLA AMO</small>
|
||||
|
||||
@ -170,25 +187,12 @@ We welcome inquiries from research groups of all sizes.<br>[See what has been fu
|
||||
<a href="https://github.com/cnourshargh/Bham-ARTIQ-examples" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="DAX - Duke ARTIQ extensions", sameheight=120) %}
|
||||
<small>A library to provide tools for system organization/abstraction and to improve usability by automating common functionality.</small>
|
||||
|
||||
<a href="https://gitlab.com/duke-artiq" target="_blank" rel="noopener noreferrer" itemprop="url">Repositories</a>
|
||||
{% end %}
|
||||
|
||||
|
||||
{% layout_card(title="Argent", sameheight=120) %}
|
||||
<small>High-level sequence control interface for ARTIQ.</small>
|
||||
|
||||
<a href="https://github.com/robertfasano/argent" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="flake8-artiq", sameheight=120) %}
|
||||
<small>A Flake8 plugin for checking ARTIQ code</small>
|
||||
|
||||
<a href="https://gitlab.com/duke-artiq/flake8-artiq" target="_blank" rel="noopener noreferrer" itemprop="url">Repository</a>
|
||||
{% end %}
|
||||
|
||||
{% layout_card(title="GenericSCPIDriver", sameheight=120) %}
|
||||
<small>A generic Python driver for SCPI devices driven over serial connections. Compatible with ARTIQ.</small>
|
||||
|
||||
|
3077
package-lock.json
generated
3077
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
29
package.json
29
package.json
@ -16,26 +16,27 @@
|
||||
"url": "https://git.m-labs.hk/M-Labs/web2019.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hello-pangea/dnd": "^16.6.0",
|
||||
"@hello-pangea/dnd": "^18.0.1",
|
||||
"bootstrap": "^5.3.3",
|
||||
"json-logic-js": "^2.0.5",
|
||||
"react": "^18.3.1",
|
||||
"react-bootstrap": "^2.10.4",
|
||||
"uuid": "^9.0.1",
|
||||
"zustand": "^4.5.4"
|
||||
"react": "^19.0.0",
|
||||
"react-bootstrap": "^2.10.9",
|
||||
"uuid": "^11.1.0",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.24.8",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/preset-react": "^7.24.7",
|
||||
"@babel/cli": "^7.26.4",
|
||||
"@babel/core": "^7.26.10",
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@babel/preset-react": "^7.26.3",
|
||||
"@uidotdev/usehooks": "^2.4.1",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-preset-minify": "^0.5.2",
|
||||
"webpack": "^5.93.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-preprocessor-loader": "^1.3.0",
|
||||
"purgecss": "^7.0.2"
|
||||
"purgecss": "^7.0.2",
|
||||
"webpack": "^5.98.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"webpack-preprocessor-loader": "^1.3.0"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
|
@ -56,6 +56,13 @@
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.card-featured {
|
||||
background: #fff url("../images/fireworks-phone@2x.png") no-repeat top center;
|
||||
background-size: contain;
|
||||
}
|
||||
.card-featured > div {
|
||||
padding-top: 66.64%;
|
||||
}
|
||||
|
||||
.card-artiq {
|
||||
background: #fff url("../images/artiq-phone@2x.png") no-repeat top center;
|
||||
@ -122,6 +129,10 @@ img.kf25 {
|
||||
// Small devices (landscape phones, 576px and up)
|
||||
@media (min-width: 576px) {
|
||||
|
||||
.card-featured > div {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.card-artiq > div {
|
||||
padding-top: 0;
|
||||
}
|
||||
@ -213,6 +224,11 @@ img.kf25 {
|
||||
box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
|
||||
}
|
||||
|
||||
.card-featured {
|
||||
background: #fff url("../images/fireworks@2x.png") no-repeat top right;
|
||||
}
|
||||
|
||||
|
||||
.card-artiq {
|
||||
background: #fff url("../images/artiq@2x.png") no-repeat top right;
|
||||
}
|
||||
|
@ -300,6 +300,9 @@ button {
|
||||
&.tabbed {
|
||||
padding-left: 16px;
|
||||
}
|
||||
&.tabbed-2 {
|
||||
padding-left: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.price {
|
||||
@ -587,6 +590,50 @@ button {
|
||||
cursor: pointer;
|
||||
padding: 0.2rem 0;
|
||||
|
||||
.shop-number {
|
||||
// Remove spin buttons
|
||||
/* Chrome, Safari, Edge, Opera */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
/* Firefox */
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 20%;
|
||||
display: inline-flex;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
padding: 0.5rem;
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
.btn:focus {
|
||||
opacity: 100%;
|
||||
}
|
||||
.btn:hover {
|
||||
opacity: 75%;
|
||||
}
|
||||
.btn:disabled {
|
||||
opacity: 40%;
|
||||
}
|
||||
|
||||
.down {
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000000'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/></svg>");
|
||||
}
|
||||
.up {
|
||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000000'><path fill-rule='evenodd' d='M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708z'/></svg>");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: .65rem;
|
||||
margin: 0;
|
||||
|
BIN
static/docs/kirdybrochure.pdf
Normal file
BIN
static/docs/kirdybrochure.pdf
Normal file
Binary file not shown.
BIN
static/images/fireworks-phone@2x.png
Normal file
BIN
static/images/fireworks-phone@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 222 KiB |
BIN
static/images/fireworks@2x.png
Normal file
BIN
static/images/fireworks@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 225 KiB |
File diff suppressed because one or more lines are too long
@ -1,9 +1,10 @@
|
||||
import {range} from "./utils";
|
||||
import React from "react";
|
||||
import React, {Fragment} from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
import {SummaryCrateHeader} from "./SummaryCrateHeader";
|
||||
import {SummaryCrateCard} from "./SummaryCrateCard";
|
||||
import {SummaryCratePricedOptions} from "./SummaryCratePricedOptions";
|
||||
import {SummaryCrateCardPricedOptions} from "./SummaryCrateCardPricedOptions";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
@ -25,7 +26,12 @@ export function SummaryCrate({crate_index}) {
|
||||
<SummaryCrateHeader crate_index={crate_index}/>
|
||||
|
||||
{range(0, crate_len).map((index, _i) =>
|
||||
<SummaryCrateCard crate_index={crate_index} card_index={index} key={"summary_crate_" + crate_id + "_" +index} />
|
||||
<Fragment key={`summary_crate_${crate_id}_${index}_group`}>
|
||||
<SummaryCrateCard crate_index={crate_index} card_index={index} key={`summary_crate_${crate_id}_${index}`} />
|
||||
|
||||
<SummaryCrateCardPricedOptions crate_index={crate_index} card_index={index}
|
||||
key={`summary_crate_${crate_id}_${index}_options`} />
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
<SummaryCratePricedOptions crate_index={crate_index}/>
|
||||
|
50
static/js/shop/SummaryCrateCardPricedOptions.jsx
Normal file
50
static/js/shop/SummaryCrateCardPricedOptions.jsx
Normal file
@ -0,0 +1,50 @@
|
||||
import {formatMoney} from "./utils";
|
||||
import {execOrReturn} from "./options/utils";
|
||||
import React from "react";
|
||||
import {useShopStore} from "./shop_store";
|
||||
|
||||
// #!render_count
|
||||
import {useRenderCount} from "@uidotdev/usehooks";
|
||||
import {ProcessOptionsToData} from "./options/Options";
|
||||
|
||||
|
||||
export function SummaryCrateCardPricedOptions({crate_index, card_index}) {
|
||||
// #!render_count
|
||||
const renderCount = useRenderCount();
|
||||
|
||||
const currency = useShopStore((state) => state.currency);
|
||||
|
||||
const crate_id = useShopStore((state) => state.crates[crate_index].id);
|
||||
|
||||
const _card_id = useShopStore((state) => state.crates[crate_index].items[card_index].id);
|
||||
const price_options = useShopStore((state) => state.crates[crate_index].items[card_index].price_options);
|
||||
const options_data = useShopStore((state) => state.crates[crate_index].items[card_index].options_data);
|
||||
const updateOptions = useShopStore((state) => state.updateOptions);
|
||||
|
||||
const processed_options = ProcessOptionsToData({options: price_options, data: options_data});
|
||||
|
||||
// #!render_count
|
||||
console.log("SummaryCrateCardPricedOptions renders: ", renderCount)
|
||||
|
||||
|
||||
return ( processed_options &&
|
||||
processed_options.map((option, i) => (
|
||||
<tr key={`summary_crate_${crate_id}_${card_index}_${i}`}>
|
||||
<td className="item-card-name tabbed-2">
|
||||
<div>{execOrReturn(option.title, options_data)}</div>
|
||||
</td>
|
||||
|
||||
<td className="price">
|
||||
<div className="d-inline-flex align-content-center">
|
||||
{`${currency} ${formatMoney(execOrReturn(option.price, options_data))}`}
|
||||
|
||||
<button onClick={() => updateOptions(crate_id, card_index, option.disable_patch)}>
|
||||
<img src="/images/shop/icon-remove.svg" className="d-block"/>
|
||||
</button>
|
||||
|
||||
<div style={{'width': '45px', 'height': '20px'}} className="d-inline"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)));
|
||||
}
|
@ -51,7 +51,7 @@ export function ProcessOptionsToData({options, data}) {
|
||||
}))
|
||||
).filter((item, _i) => !!item).flat();
|
||||
} else if (options_t === "object") {
|
||||
if (true_type_of(options.title) === "string") {
|
||||
if (true_type_of(options.title) === "string" || true_type_of(options.title) === "function") {
|
||||
return options;
|
||||
} else {
|
||||
return ProcessOptionsToData({options: json_logic_apply(options, data), data: data});
|
||||
|
94
static/js/shop/options/components/Number.jsx
Normal file
94
static/js/shop/options/components/Number.jsx
Normal file
@ -0,0 +1,94 @@
|
||||
import React, {Component} from 'react'
|
||||
import {Tip} from "./Tip";
|
||||
|
||||
export class Number extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// Initialize the state object with the initial values from the props
|
||||
this.state = {
|
||||
number: props.outvar in props.data ? props.data[props.outvar] : (props.fallback == null ? 0 : props.fallback),
|
||||
};
|
||||
// Bind the event handler to this
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleIncrement = this.handleIncrement.bind(this);
|
||||
this.handleDecrement = this.handleDecrement.bind(this);
|
||||
this.props.target.construct(this.props.outvar, this.state.number);
|
||||
}
|
||||
|
||||
handleChange(element) {
|
||||
let number = element.target.value;
|
||||
this.setState({
|
||||
number: Math.min(Math.max(this.props.min || -Infinity, number), this.props.max || Infinity),
|
||||
});
|
||||
this.props.target.update(this.props.outvar, number);
|
||||
}
|
||||
|
||||
handleIncrement() {
|
||||
let number = this.state.number + (this.props.step || 1);
|
||||
this.setState({
|
||||
number: Math.min(number, this.props.max || Infinity),
|
||||
});
|
||||
this.props.target.update(this.props.outvar, number);
|
||||
}
|
||||
|
||||
handleDecrement() {
|
||||
let number = this.state.number - (this.props.step || 1);
|
||||
this.setState({
|
||||
number: Math.max(number, this.props.min || -Infinity),
|
||||
});
|
||||
this.props.target.update(this.props.outvar, number);
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, current_state) {
|
||||
if (current_state.number !== props.data[props.outvar]) {
|
||||
return {
|
||||
number: props.data[props.outvar]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
render() {
|
||||
let key = this.props.id + this.props.outvar;
|
||||
let downDisabled = this.state.number <= this.props.min;
|
||||
let upDisabled = this.state.number >= this.props.max;
|
||||
|
||||
return (
|
||||
<div className={"shop-number " + (this.props.classes || "") } key={this.props.id}>
|
||||
<label htmlFor={key} className="form-label">
|
||||
{this.props.icon && <img src={`/images${this.props.icon}`} className="options-icon"/>}
|
||||
{this.props.title}
|
||||
{this.props.tip && <Tip id={this.props.id + "tooltip"} tip={this.props.tip}/>}
|
||||
:
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<button
|
||||
onClick={this.handleDecrement}
|
||||
className="btn down"
|
||||
disabled={downDisabled}
|
||||
/>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
value={this.state.number}
|
||||
onChange={this.handleChange}
|
||||
className="form-control"
|
||||
/>
|
||||
|
||||
<button
|
||||
onClick={this.handleIncrement}
|
||||
className="btn up"
|
||||
disabled={upDisabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function NumberWrapper(target, id, data, {title, min, max, step, outvar, fallback, icon, tip, classes}) {
|
||||
return <Number target={target} title={title} min={min} max={max} step={step} outvar={outvar} icon={icon}
|
||||
fallback={fallback} classes={classes} tip={tip} key={id}
|
||||
id={id} data={data}/>;
|
||||
}
|
@ -4,6 +4,7 @@ import {LineWrapper} from "./Line";
|
||||
import {RadioWrapper} from "./Radio";
|
||||
import {SwitchWrapper} from "./Switch";
|
||||
import {SwitchLineWrapper} from "./SwitchLine";
|
||||
import {NumberWrapper} from "./Number";
|
||||
import {UnimplementedComponent} from "./UnimplementedComponent";
|
||||
import {Label} from "./Label";
|
||||
|
||||
@ -14,6 +15,7 @@ export const componentsList = {
|
||||
"Switch": SwitchWrapper,
|
||||
"Line": LineWrapper,
|
||||
"SwitchLine": SwitchLineWrapper,
|
||||
"Number": NumberWrapper,
|
||||
"Label": Label,
|
||||
"Default": UnimplementedComponent,
|
||||
};
|
@ -4,6 +4,13 @@ import {componentsList} from "./components/components";
|
||||
// https://stackoverflow.com/a/70511311
|
||||
export const true_type_of = (obj) => Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
|
||||
|
||||
export const execOrReturn = (valuable, ...args) => {
|
||||
if (true_type_of(valuable) === "function") {
|
||||
return valuable(...args);
|
||||
}
|
||||
return valuable;
|
||||
}
|
||||
|
||||
export function FillExtCardData(data, index) {
|
||||
return {
|
||||
// we cannot use value id, because they are substituted with uuid
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import {createWithEqualityFn} from "zustand/traditional";
|
||||
import {DATA as shared_data, itemsUnfoldedList, API_RFQ} from "./utils";
|
||||
import {FillExtCrateData, FillExtOrderData, true_type_of} from "./options/utils";
|
||||
import {FillExtCrateData, FillExtOrderData, true_type_of, execOrReturn} from "./options/utils";
|
||||
import {v4 as uuidv4} from "uuid";
|
||||
import {FillResources} from "./count_resources";
|
||||
import {FillExtCardData} from "./options/utils";
|
||||
@ -458,11 +458,12 @@ const useCart = ((set, get) => ({
|
||||
crates: state.crates.map((crate, _i) => {
|
||||
if (crate_id === crate.id) {
|
||||
let itemsCopy = Array.from(crate.items);
|
||||
let options_to_place = execOrReturn(new_options, itemsCopy[index].options_data);
|
||||
itemsCopy[index] = {
|
||||
...itemsCopy[index],
|
||||
options_data: {
|
||||
...itemsCopy[index].options_data,
|
||||
...new_options
|
||||
...options_to_place
|
||||
}};
|
||||
return {
|
||||
...crate,
|
||||
@ -480,7 +481,7 @@ const useCart = ((set, get) => ({
|
||||
let itemsCopy = Array.from(crate.items);
|
||||
const disabled = !!get().crateParams(crate.crate_mode).warnings_disabled;
|
||||
itemsCopy = FillResources(itemsCopy, disabled);
|
||||
itemsCopy = TriggerWarnings(itemsCopy, disabled);
|
||||
itemsCopy = TriggerWarnings(itemsCopy, disabled, crate);
|
||||
const [crate_warnings, occupied] = TriggerCrateWarnings(crate);
|
||||
return {
|
||||
...crate,
|
||||
@ -518,13 +519,15 @@ const useCart = ((set, get) => ({
|
||||
get().crates.forEach( (crate, i) => {
|
||||
sum += get().crate_modes[crate.crate_mode].price;
|
||||
const crate_options = ProcessOptionsToData({options: get().crate_prices, data: crate.options_data || {}});
|
||||
sum += crate_options ? crate_options.reduce((accumulator, currentValue) => accumulator+currentValue.price, 0) : 0;
|
||||
sum += crate_options ? crate_options.reduce((accumulator, currentValue) => accumulator + execOrReturn(currentValue.price, crate.options_data), 0) : 0;
|
||||
crate.items.forEach((item, _) => {
|
||||
sum += item.price;
|
||||
const item_options = ProcessOptionsToData({options: item.price_options, data: item.options_data || {}});
|
||||
sum += item_options ? item_options.reduce((accumulator, currentValue) => accumulator + execOrReturn(currentValue.price, item.options_data), 0) : 0;
|
||||
});
|
||||
});
|
||||
const order_options = ProcessOptionsToData({options: get().order_prices, data: get().order_options_data || {}});
|
||||
sum += order_options ? order_options.reduce((accumulator, currentValue) => accumulator+currentValue.price, 0) : 0;
|
||||
sum += order_options ? order_options.reduce((accumulator, currentValue) => accumulator + execOrReturn(currentValue.price, get().order_options_data), 0) : 0;
|
||||
return {total_order_price: sum};
|
||||
}),
|
||||
|
||||
@ -602,6 +605,7 @@ const useCart = ((set, get) => ({
|
||||
get()._updateOptions(crate_id, index, new_options);
|
||||
get().fillExtData(crate_id);
|
||||
get().fillWarnings(crate_id);
|
||||
get()._updateTotalOrderPrice();
|
||||
},
|
||||
|
||||
initExtData: () => {
|
||||
|
@ -30,14 +30,14 @@ const find_next_source_index = (data, index, source) => {
|
||||
}
|
||||
|
||||
const not_enough_resource_trigger = (name) => {
|
||||
return (_data, _index, counters) => {
|
||||
return (_data, _index, counters, _crate) => {
|
||||
const resource = find_in_counters(counters, name);
|
||||
return resource.occupied > resource.max;
|
||||
}
|
||||
}
|
||||
|
||||
const no_source_trigger = (name) => {
|
||||
return (data, index, _counters) => {
|
||||
return (data, index, _counters, _crate) => {
|
||||
const occupied = item_occupied_counters[name](data[index]);
|
||||
if (occupied > 0)
|
||||
return !find_previous_source(data, index, name);
|
||||
@ -46,12 +46,20 @@ const no_source_trigger = (name) => {
|
||||
}
|
||||
|
||||
const wiring_constraint = (name) => {
|
||||
return (data, index, _counters) => {
|
||||
return (data, index, _counters, _crate) => {
|
||||
const next = find_next_source_index(data, index, name);
|
||||
return next - index === 1;
|
||||
}
|
||||
}
|
||||
|
||||
const phaser_next_card_long = (data, index, counters, crate) => {
|
||||
const nbrOccupied = resource_counters.hp(crate.items, -1);
|
||||
const nbrHP = useShopStore.getState().crateParams(crate.crate_mode).hp;
|
||||
return (index < data.length - 1) ? (
|
||||
(data[index+1].consumes && data[index+1].consumes.depth && data[index+1].consumes.depth >= 8.5)
|
||||
) : (nbrOccupied >= nbrHP && nbrHP > 0)
|
||||
}
|
||||
|
||||
const Types = {
|
||||
"eem_resource": {
|
||||
level: "warning",
|
||||
@ -88,22 +96,27 @@ const Types = {
|
||||
trigger: wiring_constraint("eem"),
|
||||
message: "Due to wiring constraints, the carrier can only connect to EEM cards immediately at its right, without crossing another carrier."
|
||||
},
|
||||
"phaser_next_card_long": {
|
||||
level: "reminder",
|
||||
trigger: phaser_next_card_long,
|
||||
message: "The next card may interfere with this Phaser. Consider placing a shorter card right after this Phaser."
|
||||
},
|
||||
"default": {
|
||||
level: "warning",
|
||||
trigger: (_a, _b, _c) => {
|
||||
trigger: (_a, _b, _c, _d) => {
|
||||
return true;
|
||||
},
|
||||
message: 'This item has unimplemented warning'
|
||||
}
|
||||
}
|
||||
|
||||
export function TriggerWarnings(data, disabled) {
|
||||
export function TriggerWarnings(data, disabled, crate) {
|
||||
return data.map((element, index) => {
|
||||
if (!element.warnings) return element;
|
||||
element.show_warnings = disabled ? [] :element.warnings
|
||||
.map((warning, _) => {
|
||||
if (!!Types[warning])
|
||||
return Types[warning].trigger(data, index, element.counted_resources) ? {trigger: undefined, name: warning, ...Types[warning]} : null;
|
||||
return Types[warning].trigger(data, index, element.counted_resources, crate) ? {trigger: undefined, name: warning, ...Types[warning]} : null;
|
||||
else
|
||||
return Types.default;
|
||||
})
|
||||
@ -143,6 +156,22 @@ const crate_warnings = {
|
||||
const nbrHPDesktop = useShopStore.getState().crate_modes.desktop.hp;
|
||||
return crate.crate_mode === useShopStore.getState().crate_modes.rack.id && occupied < nbrHPDesktop;
|
||||
}
|
||||
},
|
||||
"phaser_not_fit": {
|
||||
message: "Phaser cards require additional space immediately after them, but the current crate lacks sufficient room. This may disrupt placement of subsequent cards. Consider removing cards, repositioning shorter cards after Phaser(s), or using a larger crate.",
|
||||
level: "warning",
|
||||
trigger: (crate, occupied) => {
|
||||
const nbrHP = useShopStore.getState().crateParams(crate.crate_mode).hp;
|
||||
const stacked_phasers = crate.items.filter((elem, index, data) => {
|
||||
return (elem.name_codename === "Phaser") && ((index < data.length - 1) ? (
|
||||
(data[index + 1].consumes && data[index + 1].consumes.depth && data[index + 1].consumes.depth >= 8.5)
|
||||
) : (occupied >= nbrHP && nbrHP > 0))
|
||||
}
|
||||
).length;
|
||||
// #!debug
|
||||
console.log(stacked_phasers, occupied)
|
||||
return stacked_phasers > 0 && (occupied + stacked_phasers*2 >= nbrHP) && nbrHP >0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,8 @@ const shop_data = {
|
||||
"eem_wiring_constraint"
|
||||
],
|
||||
consumes: {
|
||||
hp: 8
|
||||
hp: 8,
|
||||
depth: 20
|
||||
}
|
||||
},
|
||||
'kaslisoc': {
|
||||
@ -246,22 +247,17 @@ const shop_data = {
|
||||
'Lower RTIO latency.',
|
||||
'Higher network transfer speeds.',
|
||||
'Dedicated RJ45 port for Gigabit Ethernet.',
|
||||
'4 SFP 12Gb/s slots for DRTIO.',
|
||||
'4 SFP 12.5Gb/s slots for DRTIO, and CoaXPress (ARTIQ-9+ with adapter).',
|
||||
'12 EEM connectors.',
|
||||
'4 MMCX clock outputs.',
|
||||
],
|
||||
datasheet_file: '/docs/sinara-datasheets/1125.pdf',
|
||||
datasheet_name: '1124 Carrier Kasli SoC datasheet',
|
||||
datasheet_name: '1125 Carrier Kasli SoC datasheet',
|
||||
size: 'big',
|
||||
hp: 8,
|
||||
nbrSlotMin: 0,
|
||||
nbrSlotMax: 12,
|
||||
nbrCurrentSlot: 0,
|
||||
nbrClockMax: 4,
|
||||
nbrCurrentClock: 0,
|
||||
slotOccupied: 1,
|
||||
clockOccupied: 0,
|
||||
options: [
|
||||
{type: "Number", args: {title: "CoaXPress-SFP adapters", outvar: "coaxpress", min: 0, max: 4, step: 1,
|
||||
fallback: 0, tip: "One lane of CXP-12 per adapter. ARTIQ-9 supports single lane, several may be supported later with firmware upgrade."}},
|
||||
{type: "Radio", args: {title: "DRTIO role", outvar: "drtio_role", variants: ["standalone", "master", "satellite"], tip: "Distributed Real Time Input/Output allows ARTIQ RTIO channels to be distributed among several satellite devices synchronized and controlled by a central core(master) device. Standalone option disables this feature."}},
|
||||
{
|
||||
"if": [
|
||||
@ -295,7 +291,14 @@ const shop_data = {
|
||||
]}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
],
|
||||
price_options: [
|
||||
{"if": [
|
||||
{">=": [{"var": "coaxpress"}, 1,]},
|
||||
{title: (options) => `Add CoaXPress to SFP adapter x${options.coaxpress}`, price: (options) => (1200 * options.coaxpress),
|
||||
disable_patch: {coaxpress: 0}, id: "coaxpress"}]
|
||||
},
|
||||
],
|
||||
resources: [
|
||||
{name: "eem", max: 12},
|
||||
@ -307,7 +310,8 @@ const shop_data = {
|
||||
"eem_wiring_constraint"
|
||||
],
|
||||
consumes: {
|
||||
hp: 8
|
||||
hp: 8,
|
||||
depth: 20
|
||||
}
|
||||
},
|
||||
'vhdcicarrier': {
|
||||
@ -332,7 +336,8 @@ const shop_data = {
|
||||
"eem_wiring_constraint"
|
||||
],
|
||||
consumes: {
|
||||
hp: 8
|
||||
hp: 8,
|
||||
depth: 16
|
||||
}
|
||||
},
|
||||
'bnc-dio': {
|
||||
@ -403,7 +408,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 8,
|
||||
eem: 1
|
||||
eem: 1,
|
||||
depth: 7
|
||||
}
|
||||
},
|
||||
'sma-dio': {
|
||||
@ -468,7 +474,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 1
|
||||
eem: 1,
|
||||
depth: 7
|
||||
}
|
||||
},
|
||||
'mcx-dio': {
|
||||
@ -559,7 +566,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 2
|
||||
eem: 2,
|
||||
depth: 23
|
||||
}
|
||||
},
|
||||
'rj45-dio': {
|
||||
@ -645,7 +653,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 2
|
||||
eem: 2,
|
||||
depth: 7
|
||||
}
|
||||
},
|
||||
'urukul': {
|
||||
@ -710,7 +719,8 @@ const shop_data = {
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 2,
|
||||
clk: 1
|
||||
clk: 1,
|
||||
depth: 16
|
||||
}
|
||||
},
|
||||
'urukul_4412': {
|
||||
@ -748,7 +758,8 @@ const shop_data = {
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 2,
|
||||
clk: 1
|
||||
clk: 1,
|
||||
depth: 16
|
||||
}
|
||||
},
|
||||
'phaser': {
|
||||
@ -777,12 +788,14 @@ const shop_data = {
|
||||
size: 'small',
|
||||
warnings: [
|
||||
"no_eem_source",
|
||||
"no_clk_source"
|
||||
"no_clk_source",
|
||||
"phaser_next_card_long"
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 1,
|
||||
clk: 1
|
||||
clk: 1,
|
||||
depth: 16 // heatsink 8.5
|
||||
}
|
||||
},
|
||||
'zotino': {
|
||||
@ -824,7 +837,8 @@ const shop_data = {
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 1,
|
||||
tec: 1
|
||||
tec: 1,
|
||||
depth: 16
|
||||
},
|
||||
resources: [
|
||||
{name: "idc", max: 4}
|
||||
@ -852,7 +866,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 1
|
||||
eem: 1,
|
||||
depth: 17
|
||||
},
|
||||
resources: [
|
||||
{name: "idc", max: 4}
|
||||
@ -878,7 +893,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 8,
|
||||
idc: 1
|
||||
idc: 1,
|
||||
depth: 6
|
||||
}
|
||||
},
|
||||
'hvamp32': {
|
||||
@ -901,12 +917,10 @@ const shop_data = {
|
||||
warnings: [
|
||||
"no_idc_source"
|
||||
],
|
||||
resources: [
|
||||
{name: "idc", max: 4}
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
idc: 4
|
||||
idc: 4,
|
||||
depth: 16
|
||||
}
|
||||
},
|
||||
'idc-sma-adapter': {
|
||||
@ -929,7 +943,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
idc: 1
|
||||
idc: 1,
|
||||
depth: 6
|
||||
}
|
||||
},
|
||||
'idc-mcx-adapter': {
|
||||
@ -974,7 +989,8 @@ const shop_data = {
|
||||
"idc_resource"
|
||||
],
|
||||
consumes: {
|
||||
hp: 4
|
||||
hp: 4,
|
||||
depth: 5
|
||||
},
|
||||
resources: [
|
||||
{name: "idc", max: 4}
|
||||
@ -1029,7 +1045,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 8,
|
||||
eem: 2
|
||||
eem: 2,
|
||||
depth: 16
|
||||
}
|
||||
},
|
||||
'koster': {
|
||||
@ -1055,7 +1072,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 2
|
||||
eem: 2,
|
||||
depth: 7.5
|
||||
}
|
||||
},
|
||||
'clocker': {
|
||||
@ -1088,7 +1106,8 @@ const shop_data = {
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 1,
|
||||
clk: 1
|
||||
clk: 1,
|
||||
depth: 7
|
||||
},
|
||||
resources: [
|
||||
{name: "clk", max: 6}
|
||||
@ -1125,7 +1144,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 1
|
||||
eem: 1,
|
||||
depth: 16
|
||||
},
|
||||
},
|
||||
'fast_servo': {
|
||||
@ -1159,7 +1179,8 @@ const shop_data = {
|
||||
],
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 1
|
||||
eem: 1,
|
||||
depth: 16
|
||||
},
|
||||
},
|
||||
'mirny': {
|
||||
@ -1192,7 +1213,8 @@ const shop_data = {
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 1,
|
||||
clk: 1
|
||||
clk: 1,
|
||||
depth: 16
|
||||
},
|
||||
},
|
||||
'almazny': {
|
||||
@ -1220,7 +1242,8 @@ const shop_data = {
|
||||
consumes: {
|
||||
hp: 8,
|
||||
eem: 1,
|
||||
clk: 1
|
||||
clk: 1,
|
||||
depth: 16
|
||||
},
|
||||
},
|
||||
'thermostat-eem': {
|
||||
@ -1248,6 +1271,7 @@ const shop_data = {
|
||||
consumes: {
|
||||
hp: 4,
|
||||
eem: 1,
|
||||
depth: 16
|
||||
}
|
||||
},
|
||||
'thermostat2ch': {
|
||||
@ -1266,7 +1290,8 @@ const shop_data = {
|
||||
],
|
||||
size: 'small',
|
||||
consumes: {
|
||||
hp: 4
|
||||
hp: 4,
|
||||
depth: 12
|
||||
},
|
||||
resources: [
|
||||
{name: "tec", max: 2}
|
||||
@ -1295,7 +1320,8 @@ const shop_data = {
|
||||
consumes: {
|
||||
hp: 8,
|
||||
eem: 1,
|
||||
clk: 1
|
||||
clk: 1,
|
||||
depth: 8
|
||||
},
|
||||
},
|
||||
'pounder': {
|
||||
@ -1329,7 +1355,8 @@ const shop_data = {
|
||||
consumes: {
|
||||
hp: 8,
|
||||
eem: 1,
|
||||
clk: 1
|
||||
clk: 1,
|
||||
depth: 16
|
||||
}
|
||||
},
|
||||
'eem_pwr_mod': {
|
||||
@ -1354,6 +1381,7 @@ const shop_data = {
|
||||
warnings: [],
|
||||
consumes: {
|
||||
hp: 8,
|
||||
depth: 20
|
||||
},
|
||||
},
|
||||
'kirdy': {
|
||||
@ -1385,6 +1413,7 @@ const shop_data = {
|
||||
warnings: [],
|
||||
consumes: {
|
||||
hp: 8,
|
||||
depth: 16
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -35,6 +35,42 @@
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
<div class="card shadow mt-3 mb-3" itemscope itemtype="https://schema.org/Poster">
|
||||
|
||||
<div class="card-body p-3 p-md-5 card-featured">
|
||||
|
||||
<div class="col-12 col-md-6 ps-0 pe-0">
|
||||
|
||||
<h5 class="card-title" itemprop="headline">News</h5>
|
||||
|
||||
<div class="desc-wrapper" itemprop="description">
|
||||
<p class="card-text pt-3">
|
||||
The Sinara 1550 Kirdy is a laser diode driver which combines a low-noise current source and a precision temperature controller. In combination with Fast-Servo, it can lock lasers to spectral lines and reference cavities. See <a href="docs/kirdybrochure.pdf">the brochure</a>.
|
||||
</p>
|
||||
|
||||
<p class="card-text pt-3">
|
||||
The world's <a href="https://www.ox.ac.uk/news/2025-02-06-first-distributed-quantum-algorithm-brings-quantum-supercomputers-closer">first distributed quantum algorithm experiment</a> was performed at Oxford University using ARTIQ. See <a href="https://www.nature.com/articles/s41586-024-08404-x">the paper</a>.
|
||||
</p>
|
||||
|
||||
<p class="card-text pt-3">
|
||||
The Sinara 5716 Shuttler is a 16-channel 125MSPS 14-bit DAC optimized for trapped ion shuttling. More information in the <a href="https://forum.m-labs.hk/d/745-artiq-8-released">ARTIQ-8 release announcement</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row pb-5">
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
<div class="card shadow mt-3 mb-3" itemscope itemtype="https://schema.org/Poster">
|
||||
|
Loading…
x
Reference in New Issue
Block a user