Skip to content

Commit

Permalink
Merge pull request #31 from ialokim/set-bits-fixes
Browse files Browse the repository at this point in the history
Fixes to change bits after initialization
  • Loading branch information
tilk authored Sep 27, 2020
2 parents 8cdce53 + ee28c23 commit 8faa6c7
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 56 deletions.
38 changes: 29 additions & 9 deletions examples/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div>
<button name="start" type="button">▶️</button>
<button name="stop" type="button">⏹️</button>
</div>
<div id="paper">
</div>
<div>
Expand All @@ -21,27 +25,43 @@
</div>
<script>
var circuit, monitor, monitorview, iopanel, paper;
var subpapers = {};
var start = $('button[name=start]');
var stop = $('button[name=stop]');
var papers = {};
const fixed = function (fixed) {
paper.fixed(fixed);
Object.values(subpapers).forEach(p => p.fixed(fixed));
Object.values(papers).forEach(p => p.fixed(fixed));
}
const loadCircuit = function (json) {
circuit = new digitaljs.Circuit(json);
monitor = new digitaljs.Monitor(circuit);
monitorview = new digitaljs.MonitorView({model: monitor, el: $('#monitor') });
iopanel = new digitaljs.IOPanelView({model: circuit, el: $('#iopanel') });
circuit.on('new:paper', function(paper) {
paper.fixed($('input[name=fixed]').prop('checked'));
papers[paper.cid] = paper;
paper.on('element:pointerdblclick', (cellView) => {
window.digitaljsCell = cellView.model;
console.info('You can now access the doubly clicked gate as digitaljsCell in your WebBrowser console!');
});
});
circuit.on('changeRunning', () => {
if (circuit.running) {
start.prop('disabled', true);
stop.prop('disabled', false);
} else {
start.prop('disabled', false);
stop.prop('disabled', true);
}
});
paper = circuit.displayOn($('#paper'));
fixed($('input[name=fixed]').prop('checked'));
circuit.on('new:paper', function(subpaper) {
subpaper.fixed($('input[name=fixed]').prop('checked'));
subpapers[subpaper.cid] = subpaper;
});
circuit.on('remove:paper', function(subpaper) {
delete subpapers[subpaper.cid];
circuit.on('remove:paper', function(paper) {
delete papers[paper.cid];
});
circuit.start();
}
start.on('click', (e) => { circuit.start(); });
stop.on('click', (e) => { circuit.stop(); });
$('button[name=json]').on('click', (e) => {
monitorview.shutdown();
iopanel.shutdown();
Expand Down
105 changes: 67 additions & 38 deletions src/cells/base.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,14 @@ export const Gate = joint.shapes.basic.Generic.define('Gate', {

this.bindAttrToProp('label/text', 'label');
if (this.unsupportedPropChanges.length > 0) {
this.on(this.unsupportedPropChanges.map(prop => 'change:'+prop).join(' '), function(model, _, opt) {
this.on(this.unsupportedPropChanges.map(prop => 'change:'+prop).join(' '), (__, ___, opt) => {
if (opt.init) return;

if (opt.propertyPath)
console.warn('Beta property change support: "' + opt.propertyPath + '" changes on ' + model.prop('type') + ' are currently not reflected.');
else
console.warn('Beta property change support: changes on ' + model.prop('type') + ' are currently not reflected. Also consider using Cell.prop() instead of Model.set().');
const changed = _.intersection(Object.keys(this.changed), this.unsupportedPropChanges);
changed.forEach(attr => {
this.set(attr, this.previous(attr), {init: true});
});
console.warn('Beta property change support: "' + changed + '" changes on ' + this.get('type') + ' are (currently) not supported.');
});
}
},
Expand All @@ -127,11 +128,8 @@ export const Gate = joint.shapes.basic.Generic.define('Gate', {
this.on('change:' + prop, (_, val) => this.attr(attr, val));
},
_preprocessPorts: function(ports) {
this.resetPortsSignals(ports);
for (const port of ports) {
const signame = port.dir === 'in' ? 'inputSignals' : 'outputSignals';
this.get(signame)[port.id] = Vector3vl.xes(port.bits);
console.assert(port.bits > 0);

port.attrs = {};
port.attrs['bits'] = { text: this.getBitsText(port.bits) }
if (port.labelled) {
Expand All @@ -150,34 +148,56 @@ export const Gate = joint.shapes.basic.Generic.define('Gate', {
}
},
setPortsBits: function(portsBits) {
const ports = this.get('ports');
const ports = _.cloneDeep(this.get('ports'));
const portsReset = [];
for (const portid in portsBits) {
const bits = portsBits[portid];
const port = ports.items.find(function(port) {
return port.id && port.id === portid;
});
port.bits = bits;
port.attrs['bits'].text = this.getBitsText(bits);

const signame = port.dir === 'in' ? 'inputSignals' : 'outputSignals';
const signals = this.get(signame);
signals[portid] = Vector3vl.xes(bits);
this.set(signame, signals);

this.graph.getConnectedLinks(this, { outbound: port.dir === 'out', inbound: port.dir === 'in' })
.filter((wire) => wire.get(port.dir === 'out' ? 'source' : 'target').port === portid)
.forEach((wire) => wire.checkConnection());
portsReset.push(port);
}
this.resetPortsSignals(portsReset);
//trigger port changes on model and view
this.trigger('change:ports', this, ports);
this.set('ports', ports);
this.graph.getConnectedLinks(this, { outbound: true })
.filter((wire) => wire.get('source').port in portsBits)
.forEach((wire) => wire.changeSource(wire.get('source')));
this.graph.getConnectedLinks(this, { inbound: true })
.filter((wire) => wire.get('target').port in portsBits)
.forEach((wire) => wire.changeTarget(wire.get('target')));
},
getBitsText: function(bits) {
return bits > 1 ? bits : '';
},
removePortSignals: function(port) {
port = port.id !== undefined ? port : this.getPort(port);
const signame = port.dir === 'in' ? 'inputSignals' : 'outputSignals';
this.removeProp([signame, port.id]);
resetPortsSignals: function(ports) {
const signals = {
in: this.get('inputSignals'),
out: this.get('outputSignals')
}

for (const port of ports) {
console.assert(port.bits > 0);
signals[port.dir][port.id] = Vector3vl.xes(port.bits);
}

this.set('inputSignals', signals.in);
this.set('outputSignals', signals.out);
},
removePortsSignals: function(ports) {
const signals = {
in: this.get('inputSignals'),
out: this.get('outputSignals')
}

for (const port of ports) {
delete signals[port.dir][port.id];
}

this.set('inputSignals', signals.in);
this.set('outputSignals', signals.out);
},
addPort: function(port) {
this.addPorts([port]);
Expand All @@ -187,11 +207,10 @@ export const Gate = joint.shapes.basic.Generic.define('Gate', {
joint.shapes.basic.Generic.prototype.addPorts.apply(this, arguments);
},
removePort: function(port, opt) {
this.removePortSignals(port);
joint.shapes.basic.Generic.prototype.removePort.apply(this, arguments);
this.removePorts([port]);
},
removePorts: function(ports, opt) {
ports.forEach((port) => this.removePortSignals(port), this);
this.removePortsSignals(ports);
joint.shapes.basic.Generic.prototype.removePorts.apply(this, arguments);
},
getStackedPosition: function(opt) {
Expand Down Expand Up @@ -306,9 +325,11 @@ export const GateView = joint.dia.ElementView.extend({
}
},
applyPortAttrs(port, attrs) {
for (const selector in attrs) {
const node = this._portElementsCache[port].portSelectors[selector];
this.setNodeAttributes(node, attrs[selector]);
if (port in this._portElementsCache) {
for (const selector in attrs) {
const node = this._portElementsCache[port].portSelectors[selector];
this.setNodeAttributes(node, attrs[selector]);
}
}
},
_updatePorts() {
Expand Down Expand Up @@ -372,12 +393,20 @@ export const Wire = joint.shapes.standard.Link.define('Wire', {
}
joint.shapes.standard.Link.prototype.remove.apply(this, arguments);
},
changeSignal(sig) {
propagateSignal(tar, sig) {
const target = this.getTargetElement();
if (target) target.setInput(sig, this.get('target').port);
if (target) {
if (this.get('warning'))
target.clearInput(tar.port);
else
target.setInput(sig, tar.port);
}
},
changeSignal(sig) {
this.propagateSignal(this.get('target'), sig);
},
changeSource(src) {
const source = this.graph.getCell(src.id);
const source = this.getSourceElement();
if (source && 'port' in src) {
this.set('bits', source.getPort(src.port).bits);
this.checkConnection();
Expand All @@ -388,11 +417,11 @@ export const Wire = joint.shapes.standard.Link.define('Wire', {
}
},
changeTarget(tar) {
const target = this.graph.getCell(tar.id);
if (target && 'port' in tar) {
target.setInput(this.get('signal'), tar.port);
}
this.checkConnection();
if ('port' in tar) {
this.propagateSignal(tar, this.get('signal'));
}
if (!this.hasChanged('target')) return;
const preTar = this.previous('target');
const preTarget = this.graph.getCell(preTar.id);
if (preTarget && 'port' in preTar) {
Expand All @@ -402,7 +431,7 @@ export const Wire = joint.shapes.standard.Link.define('Wire', {
checkConnection() {
const tar = this.get('target');
const target = this.graph.getCell(tar.id);
this.set('warning', target && target.getPort(tar.port).bits !== this.get('bits'));
this.set('warning', (target && target.getPort(tar.port).bits !== this.get('bits')) || false);
},
getWireParams: function(layout) {
const connector = {
Expand Down
2 changes: 1 addition & 1 deletion src/cells/bus.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const BusSlice = Box.define('BusSlice', {

Box.prototype.initialize.apply(this, arguments);

this.on('change:extend', (_, extend) => {
this.on('change:slice', (_, slice) => {
this.setPortsBits({ in: slice.total, out: slice.count });
});
},
Expand Down
27 changes: 20 additions & 7 deletions src/cells/io.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,6 @@ export const NumEntry = NumBase.define('NumEntry', {
this.on('change:bits', (_, bits) => {
this.setPortsBits({ out: bits });
});

this.set('buttonState', this.get('outputSignals').out);
},
operation: function() {
return { out: this.get('buttonState') };
Expand Down Expand Up @@ -257,7 +255,8 @@ export const Lamp = Box.define('Lamp', {
]),
getLogicValue: function() {
return this.get('inputSignals').in;
}
},
unsupportedPropChanges: Box.prototype.unsupportedPropChanges.concat(['bits'])
});
export const LampView = BoxView.extend({
attrs: _.merge({}, BoxView.prototype.attrs, {
Expand Down Expand Up @@ -323,7 +322,8 @@ export const Button = Box.define('Button', {
if (sig.bits != 1)
throw new Error("setLogicValue: wrong number of bits");
this.set('buttonState', sig.isHigh);
}
},
unsupportedPropChanges: Box.prototype.unsupportedPropChanges.concat(['bits'])
});
export const ButtonView = BoxView.extend({
attrs: _.merge({}, BoxView.prototype.attrs, {
Expand Down Expand Up @@ -392,6 +392,15 @@ export const IO = Box.define('IO', {
});
this.bindAttrToProp('text.ioname/text', 'net');
},
setPortsBits: function(portsBits) {
Box.prototype.setPortsBits.apply(this, arguments);

const subcir = this.graph.get('subcircuit');
if (subcir == null) return; // not inside a subcircuit
const portsBitsSubcir = {};
portsBitsSubcir[this.get('net')] = portsBits[this.io_dir];
subcir.setPortsBits(portsBitsSubcir);
},
markup: Box.prototype.markup.concat([{
tagName: 'text',
className: 'ioname',
Expand Down Expand Up @@ -479,14 +488,17 @@ export const Constant = NumBase.define('Constant', {
numbaseType: 'show'
});
export const ConstantView = NumBaseView.extend({
presentationAttributes: NumBaseView.addPresentationAttributes({
constantCache: 'CONSTANT'
}),
confirmUpdate(flags) {
NumBaseView.prototype.confirmUpdate.apply(this, arguments);
if (this.hasFlag(flags, 'SIGNAL2') ||
if (this.hasFlag(flags, 'CONSTANT') ||
this.hasFlag(flags, 'NUMBASE')) this.settext();
},
settext() {
const display3vl = this.model.graph._display3vl;
this.$('text.value tspan').text(display3vl.show(this.model.get('numbase'), this.model.get('outputSignals').out));
this.$('text.value tspan').text(display3vl.show(this.model.get('numbase'), this.model.get('constantCache')));
},
update() {
NumBaseView.prototype.update.apply(this, arguments);
Expand Down Expand Up @@ -534,7 +546,8 @@ export const Clock = Box.define('Clock', {
}]
}]
}
])
]),
unsupportedPropChanges: Box.prototype.unsupportedPropChanges.concat(['bits'])
});
export const ClockView = BoxView.extend({
presentationAttributes: BoxView.addPresentationAttributes({
Expand Down
6 changes: 5 additions & 1 deletion src/circuit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,14 @@ export class HeadlessCircuit {
const graph = new joint.dia.Graph();
graph._display3vl = this._display3vl;
graph._warnings = 0;
this.listenTo(graph, 'change:buttonState', (gate, sig) => {
this.listenTo(graph, 'change:buttonState', (gate) => {
// buttonState is triggered for any user change on inputs
this.enqueue(gate);
this.trigger('userChange');
});
this.listenTo(graph, 'change:constantCache', (gate) => {
this.enqueue(gate);
});
this.listenTo(graph, 'change:inputSignals', (gate, sigs) => {
if (gate.changeInputSignals) {
gate.changeInputSignals(sigs);
Expand All @@ -118,6 +121,7 @@ export class HeadlessCircuit {
if (cell.previous('warning') === warn)
return;
graph._warnings += warn ? 1 : -1;
console.assert(graph._warnings >= 0);

//todo: better handling for stopping simulation
if (graph._warnings > 0 && this.running)
Expand Down

0 comments on commit 8faa6c7

Please sign in to comment.