You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
4.6 KiB

import 'package:logic_circuits_simulator/models.dart';
import 'package:logic_circuits_simulator/state/component.dart';
import 'package:logic_circuits_simulator/utils/iterable_extension.dart';
import 'package:logic_circuits_simulator/utils/logic_expressions.dart';
class SimulatedComponent {
final ProjectEntry project;
final ComponentEntry component;
final ComponentState? state;
final Future<SimulatedComponent> Function(String depId) onRequiredDependency;
final _instances = <String, SimulatedComponent>{};
SimulatedComponent({
required this.project,
required this.component,
required this.onRequiredDependency,
this.state
});
Future<SimulatedComponent> _getInstance(String instanceId, String? depId) async {
if (!_instances.containsKey(instanceId)) {
if (depId != null) {
_instances[instanceId] = await onRequiredDependency(depId);
}
else {
throw Exception('Attempted to get instance of unknown component');
}
}
return _instances[instanceId]!;
}
Future<Map<String, bool>> simulate(Map<String, bool> inputs) async {
final input = int.parse(component.inputs.map((input) => inputs[input]! ? '1' : '0').join(), radix: 2);
if (component.truthTable != null) {
final output = component.truthTable![input];
return {
for (final it in component.outputs.indexedMap((index, outName) => [outName, output[index]]))
it[0] : it[1] == '1'
};
}
else if (component.logicExpression != null) {
// Somehow?
// A truth table should be automatically generated for every logic expression component.
// Might as well handle cases where that isn't done anyway.
final results = component.outputs.zipWith(
[component.logicExpression!],
(zips) {
final output = zips[0];
final le = LogicExpression.parse(zips[1]);
return [output, le.evaluate(inputs)];
},
);
return {
for (final it in results)
it[0] as String : it[1] as bool
};
}
else if (state == null) {
throw Exception('Cannot simulate designed component without its state');
}
else {
// Create instances
final wiring = state!.wiring;
for (final instance in wiring.instances) {
_getInstance(instance.instanceId, instance.componentId);
}
// Simulate
final requiredSinks = <String>[
...component.outputs.map((output) => 'self/$output'),
];
final knownSources = {
for (final entry in inputs.entries)
'self/${entry.key}': entry.value
};
final knownSinks = <String, bool>{};
while (requiredSinks.isNotEmpty) {
final sink = requiredSinks.removeAt(0);
// C-SOURCE - WIRE-OUT -> WIRE-IN - C-SINK
if (knownSinks.containsKey(sink)) {
// Requirement satisfied
continue;
}
else {
// Find wire that provides sink
final wire = wiring.wires.where((wire) => wire.input == sink).first;
if (knownSources.containsKey(wire.output)) {
// If we know the output provided through the wire,
// we know the input provided to the sink
knownSinks[sink] = knownSources[wire.output]!;
}
else {
// The instance providing the source for the wire has not been simulated.
// See if all its sinks are known:
final instanceId = wire.output.split('/')[0];
final instance = await _getInstance(instanceId, null);
final depSinks = instance.component.inputs.map((input) => '$instanceId/$input').toList();
if (depSinks.map((depSink) => !knownSinks.containsKey(depSink)).where((cond) => cond).isEmpty) {
// If so, then simulate
final results = await instance.simulate({
for (final depSink in depSinks)
depSink.split('/')[1] : knownSinks[depSink]!
});
knownSources.addAll({
for (final result in results.entries)
'$instanceId/${result.key}' : result.value
});
// And resolve needed sink
knownSinks[sink] = knownSources[wire.output]!;
}
else {
// Otherwise, require the sinks and reschedule the current one
requiredSinks.addAll(depSinks.where((depSink) => !knownSinks.containsKey(depSink)));
requiredSinks.add(sink);
}
}
}
}
return {
for (final output in component.outputs)
output : knownSinks['self/$output']!
};
}
}
}