function petriNetMutexDemo % PETRINETMUTEXDEMO GUI demo: two machines sharing one robot arm. % The token moves from the central resource place to Machine A or B. fig = figure( ... 'Name', 'Two Machines Sharing One Robot Arm', ... 'Color', 'w', ... 'NumberTitle', 'off', ... 'MenuBar', 'none', ... 'ToolBar', 'none', ... 'Resize', 'off', ... 'Position', [100 100 1020 590]); ax = axes( ... 'Parent', fig, ... 'Position', [0.04 0.23 0.92 0.69]); hold(ax, 'on'); axis(ax, [0 10 0 6]); axis(ax, 'equal'); axis(ax, 'off'); % ------------------------------------------------------------------------- % Model state % ------------------------------------------------------------------------- state.aReady = false; state.bReady = false; state.aActive = false; state.bActive = false; state.aWaiting = false; state.bWaiting = false; state.resourceFree = true; state.tokenLocation = 0; % 0=center, 1=left, 2=right state.message = 'Press Ready A and Ready B, then fire the enabled transition.'; % ------------------------------------------------------------------------- % Layout % ------------------------------------------------------------------------- leftPlace = [0.9 1.05 1.8 1.8]; centerPlace = [4.35 3.15 1.3 1.3]; rightPlace = [7.3 1.05 1.8 1.8]; tAPos = [3.0 2.55 0.35 0.85]; tBPos = [6.65 2.55 0.35 0.85]; leftCenter = [leftPlace(1) + leftPlace(3) / 2, leftPlace(2) + leftPlace(4) / 2]; centerCenter = [centerPlace(1) + centerPlace(3) / 2, centerPlace(2) + centerPlace(4) / 2]; rightCenter = [rightPlace(1) + rightPlace(3) / 2, rightPlace(2) + rightPlace(4) / 2]; tokenR = 0.11; % Header text(ax, 5.0, 5.72, ... 'Green = executing | Yellow = enabled | Red = waiting | Token moves from center to the chosen machine', ... 'HorizontalAlignment', 'center', 'FontSize', 11, 'FontWeight', 'bold', 'Interpreter', 'none'); text(ax, 5.0, 5.40, 'Shared robot arm token: M_0 = 1', ... 'HorizontalAlignment', 'center', 'FontSize', 10, 'Interpreter', 'none'); % Arcs h.arcCenterA = drawArrow([4.75 3.15], [3.18 3.40]); h.arcAToLeft = drawArrow([3.18 2.55], [1.95 2.05]); h.arcCenterB = drawArrow([5.25 3.15], [6.82 3.40]); h.arcBToRight = drawArrow([6.82 2.55], [8.05 2.05]); % Places and transitions h.left = rectangle(ax, 'Position', leftPlace, 'Curvature', [1 1], ... 'FaceColor', [1 1 1], 'EdgeColor', [0.25 0.25 0.25], 'LineWidth', 2); h.center = rectangle(ax, 'Position', centerPlace, 'Curvature', [1 1], ... 'FaceColor', [1.0 0.95 0.65], 'EdgeColor', [0.45 0.35 0.0], 'LineWidth', 2); h.right = rectangle(ax, 'Position', rightPlace, 'Curvature', [1 1], ... 'FaceColor', [1 1 1], 'EdgeColor', [0.25 0.25 0.25], 'LineWidth', 2); h.tA = rectangle(ax, 'Position', tAPos, ... 'FaceColor', [0.25 0.25 0.25], 'EdgeColor', [0.15 0.15 0.15], 'LineWidth', 1.5); h.tB = rectangle(ax, 'Position', tBPos, ... 'FaceColor', [0.25 0.25 0.25], 'EdgeColor', [0.15 0.15 0.15], 'LineWidth', 1.5); % Token h.token = rectangle(ax, ... 'Position', [centerCenter(1) - tokenR, centerCenter(2) - tokenR, 2 * tokenR, 2 * tokenR], ... 'Curvature', [1 1], ... 'FaceColor', [1.0 0.85 0.1], ... 'EdgeColor', 'none'); % Labels text(ax, leftCenter(1), leftCenter(2) + 0.95, 'P_{machineA\_usingRobot}', ... 'HorizontalAlignment', 'center', 'FontSize', 11, 'Interpreter', 'tex'); text(ax, centerCenter(1), centerCenter(2) + 0.95, 'P_{robotArm}', ... 'HorizontalAlignment', 'center', 'FontSize', 11, 'Interpreter', 'tex'); text(ax, rightCenter(1), rightCenter(2) + 0.95, 'P_{machineB\_usingRobot}', ... 'HorizontalAlignment', 'center', 'FontSize', 11, 'Interpreter', 'tex'); text(ax, 3.18, 3.72, 'T_{startA}', 'HorizontalAlignment', 'center', 'FontSize', 11, 'Interpreter', 'tex'); text(ax, 6.82, 3.72, 'T_{startB}', 'HorizontalAlignment', 'center', 'FontSize', 11, 'Interpreter', 'tex'); % Buttons uicontrol(fig, 'Style', 'pushbutton', 'String', 'Ready A', ... 'Position', [70 70 110 32], 'Callback', @(~, ~) makeReady(1)); uicontrol(fig, 'Style', 'pushbutton', 'String', 'Ready B', ... 'Position', [190 70 110 32], 'Callback', @(~, ~) makeReady(2)); uicontrol(fig, 'Style', 'pushbutton', 'String', 'Fire Enabled Transition', ... 'Position', [320 70 180 32], 'Callback', @(~, ~) fireTransition()); uicontrol(fig, 'Style', 'pushbutton', 'String', 'Complete Task', ... 'Position', [515 70 140 32], 'Callback', @(~, ~) completeTask()); uicontrol(fig, 'Style', 'pushbutton', 'String', 'Reset', ... 'Position', [670 70 90 32], 'Callback', @(~, ~) resetDemo()); status = uicontrol(fig, 'Style', 'text', ... 'Position', [35 18 950 30], ... 'BackgroundColor', 'w', ... 'HorizontalAlignment', 'left', ... 'FontSize', 11); updateView(); function makeReady(machineIdx) if machineIdx == 1 if ~state.aActive state.aReady = true; state.aWaiting = false; end state.message = 'Machine A is ready to request the robot arm.'; else if ~state.bActive state.bReady = true; state.bWaiting = false; end state.message = 'Machine B is ready to request the robot arm.'; end updateView(); end function fireTransition() enabled = []; if state.resourceFree && state.aReady && ~state.aActive enabled(end + 1) = 1; %#ok end if state.resourceFree && state.bReady && ~state.bActive enabled(end + 1) = 2; %#ok end if isempty(enabled) state.message = 'No enabled transition: make a machine ready and keep the robot arm free.'; updateView(); return; end if numel(enabled) == 2 if rand < 0.5 chosen = 1; loser = 2; else chosen = 2; loser = 1; end state.message = sprintf('Both transitions are enabled. T_start%d won the random choice.', chosen); setActive(chosen); setWaiting(loser); else chosen = enabled(1); state.message = sprintf('Only T_start%d is enabled, so it fires.', chosen); setActive(chosen); end state.resourceFree = false; animateTokenMove(chosen); state.tokenLocation = chosen; updateView(); end function setActive(machineIdx) if machineIdx == 1 state.aReady = false; state.aActive = true; state.aWaiting = false; else state.bReady = false; state.bActive = true; state.bWaiting = false; end end function setWaiting(machineIdx) if machineIdx == 1 state.aWaiting = true; else state.bWaiting = true; end end function completeTask() if state.aActive state.aActive = false; state.message = 'Machine A finished. The robot arm token returns to the center.'; elseif state.bActive state.bActive = false; state.message = 'Machine B finished. The robot arm token returns to the center.'; else state.message = 'No machine is currently using the robot arm.'; updateView(); return; end state.resourceFree = true; animateTokenMove(0); state.tokenLocation = 0; if state.aWaiting state.aWaiting = false; state.aReady = true; end if state.bWaiting state.bWaiting = false; state.bReady = true; end updateView(); end function resetDemo() state.aReady = false; state.bReady = false; state.aActive = false; state.bActive = false; state.aWaiting = false; state.bWaiting = false; state.resourceFree = true; state.tokenLocation = 0; state.message = 'Reset complete. Press Ready A and Ready B, then fire the enabled transition.'; updateTokenPosition(0); updateView(); end function updateView() enabledA = state.resourceFree && state.aReady && ~state.aActive; enabledB = state.resourceFree && state.bReady && ~state.bActive; set(h.left, 'FaceColor', getMachineColor(state.aReady, state.aActive, state.aWaiting)); set(h.right, 'FaceColor', getMachineColor(state.bReady, state.bActive, state.bWaiting)); set(h.center, 'FaceColor', getResourceColor(state.resourceFree)); set(h.tA, 'FaceColor', getTransitionColor(enabledA, state.aActive, state.aWaiting)); set(h.tB, 'FaceColor', getTransitionColor(enabledB, state.bActive, state.bWaiting)); if state.resourceFree set(h.token, 'Visible', 'on'); updateTokenPosition(0); else set(h.token, 'Visible', 'on'); updateTokenPosition(state.tokenLocation); end if enabledA && enabledB setArrowStyle(h.arcCenterA, [1.0 0.55 0.0], 3.0); setArrowStyle(h.arcCenterB, [1.0 0.55 0.0], 3.0); elseif enabledA setArrowStyle(h.arcCenterA, [1.0 0.55 0.0], 3.0); setArrowStyle(h.arcCenterB, [0.65 0.65 0.65], 1.2); elseif enabledB setArrowStyle(h.arcCenterA, [0.65 0.65 0.65], 1.2); setArrowStyle(h.arcCenterB, [1.0 0.55 0.0], 3.0); else setArrowStyle(h.arcCenterA, [0.65 0.65 0.65], 1.2); setArrowStyle(h.arcCenterB, [0.65 0.65 0.65], 1.2); end setArrowStyle(h.arcAToLeft, [0.55 0.55 0.55], 1.2); setArrowStyle(h.arcBToRight, [0.55 0.55 0.55], 1.2); set(status, 'String', state.message); drawnow; end function updateTokenPosition(location) if location == 0 c = centerCenter; elseif location == 1 c = leftCenter; else c = rightCenter; end set(h.token, 'Position', [c(1) - tokenR, c(2) - tokenR, 2 * tokenR, 2 * tokenR]); end function animateTokenMove(location) if location == 0 target = centerCenter; elseif location == 1 target = leftCenter; else target = rightCenter; end if state.tokenLocation == 0 start = centerCenter; elseif state.tokenLocation == 1 start = leftCenter; else start = rightCenter; end nSteps = 20; for k = 1:nSteps alpha = k / nSteps; c = (1 - alpha) * start + alpha * target; set(h.token, 'Position', [c(1) - tokenR, c(2) - tokenR, 2 * tokenR, 2 * tokenR]); drawnow; end end function c = getMachineColor(isReady, isActive, isWaiting) if isActive c = [0.2 0.8 0.2]; elseif isWaiting c = [0.92 0.35 0.35]; elseif isReady c = [0.5 0.8 1.0]; else c = [1.0 1.0 1.0]; end end function c = getResourceColor(isFree) if isFree c = [1.0 0.95 0.65]; else c = [0.82 0.82 0.82]; end end function c = getTransitionColor(isEnabled, isActive, isWaiting) if isActive c = [0.2 0.8 0.2]; elseif isWaiting c = [0.92 0.35 0.35]; elseif isEnabled c = [1.0 0.85 0.25]; else c = [0.25 0.25 0.25]; end end function setArrowStyle(hArrow, color, width) set(hArrow.line, 'Color', color, 'LineWidth', width); set(hArrow.head, 'FaceColor', color, 'EdgeColor', color); end function hArrow = drawArrow(p1, p2) hArrow.line = plot(ax, [p1(1) p2(1)], [p1(2) p2(2)], ... 'k-', 'LineWidth', 1.5); v = p2 - p1; u = v / norm(v); n = [-u(2) u(1)]; tip = p2; base = p2 - 0.18 * u; left = base + 0.08 * n; right = base - 0.08 * n; hArrow.head = patch(ax, [tip(1) left(1) right(1)], ... [tip(2) left(2) right(2)], 'k', 'EdgeColor', 'k'); end end