From e4b3b18605a0491bc3b7a638f336dcba84b97ff9 Mon Sep 17 00:00:00 2001 From: REllEK-IO Date: Thu, 12 Oct 2023 10:52:04 -0700 Subject: [PATCH] Updated principles and added register StagePlanner --- package.json | 2 +- src/concepts/axium/axium.concept.ts | 22 +- src/concepts/axium/axium.mode.ts | 2 +- src/concepts/axium/axium.principle.ts | 14 +- .../qualities/addConceptsFromQue.quality.ts | 2 +- src/concepts/axium/qualities/close.quality.ts | 12 +- .../qualities/initializePrinciples.quality.ts | 4 +- .../qualities/registerStagePlanner.quality.ts | 32 +++ ...ity.ts => registerSubscription.quality.ts} | 8 +- .../qualities/removeConceptsViaQue.quality.ts | 29 ++- .../qualities/setBlockingMode.quality.ts | 4 +- .../axium/qualities/setDefaultMode.quality.ts | 4 +- src/concepts/chain/chain.principle.ts | 20 +- .../experiment/experiment.principle.ts | 69 ++++--- src/concepts/ownership/ownership.mode.ts | 2 +- src/concepts/ownership/ownership.principle.ts | 194 +++++++++--------- src/concepts/ownership/ownership.selector.ts | 7 +- src/index.ts | 15 +- src/model/axium.ts | 4 +- src/model/concept.ts | 2 +- src/model/principle.ts | 11 +- .../{unifiedSubject.ts => stagePlanner.ts} | 49 ++++- src/test/stagedPrinciple.test.ts | 2 +- 23 files changed, 306 insertions(+), 204 deletions(-) create mode 100644 src/concepts/axium/qualities/registerStagePlanner.quality.ts rename src/concepts/axium/qualities/{registerSubscriber.quality.ts => registerSubscription.quality.ts} (85%) rename src/model/{unifiedSubject.ts => stagePlanner.ts} (86%) diff --git a/package.json b/package.json index 06a983e..dc29559 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@phuire/strx", - "version": "0.0.26", + "version": "0.0.27", "description": "Unified Turing Machine", "main": "dist/index.js", "module": "dist/index.mjs", diff --git a/src/concepts/axium/axium.concept.ts b/src/concepts/axium/axium.concept.ts index 5548f00..4eb608d 100644 --- a/src/concepts/axium/axium.concept.ts +++ b/src/concepts/axium/axium.concept.ts @@ -1,4 +1,4 @@ -import { Subject, Subscriber } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; import { Concept } from '../../model/concept'; import { Action } from '../../model/action'; import { axiumPrinciple } from './axium.principle'; @@ -7,7 +7,7 @@ import { openQuality } from './qualities/open.quality'; import { badActionQuality } from './qualities/badAction.quality'; import { closeQuality } from './qualities/close.quality'; import { logQuality } from './qualities/log.quality'; -import { registerSubscriberQuality } from './qualities/registerSubscriber.quality'; +import { registerSubscriberQuality } from './qualities/registerSubscription.quality'; import { initializePrinciplesQuality } from './qualities/initializePrinciples.quality'; export { initializationStrategy } from './strategies/initialization.strategy'; import { setBlockingModeQuality } from './qualities/setBlockingMode.quality'; @@ -21,14 +21,15 @@ import { createConcept } from '../../model/concept'; import { setModeQuality } from './qualities/setMode.quality'; import { setDefaultModeIndexQuality } from './qualities/setDefaultModeIndex.quality'; import { clearDialogQuality } from './qualities/clearDialog.quality'; -import { Plan, UnifiedSubject } from '../../model/unifiedSubject'; +import { NamedStagePlanner, Plan, UnifiedSubject } from '../../model/stagePlanner'; import { clearBadActionTypeFromBadActionListQuality } from './qualities/clearBadActionTypeFromBadActionList.quality'; import { clearBadStrategyTopicFromBadActionListQuality } from './qualities/clearBadStrategyTopicFromBadActionList.quality'; import { clearBadPlanFromBadPlanListQuality } from './qualities/clearBadPlanFromBadPlanList.quality'; +import { registerStagePlannerQuality } from './qualities/registerStagePlanner.quality'; -export type NamedSubscriber = { +export type NamedSubscription = { name: string; - subscriber: Subscriber; + subscription: Subscription; } export type AxiumState = { @@ -44,8 +45,9 @@ export type AxiumState = { modeIndex: number; defaultModeIndex: number; modeNames: string[] - methodSubscribers: NamedSubscriber[]; - generalSubscribers: NamedSubscriber[]; + methodSubscribers: NamedSubscription[]; + generalSubscribers: NamedSubscription[]; + stagePlanners: NamedStagePlanner[]; action$: Subject; concepts$: UnifiedSubject; addConceptQue: Concept[], @@ -70,8 +72,9 @@ const createAxiumState = (name: string, storeDialog?: boolean, logging?: boolean modeIndex: 0, defaultModeIndex: 1, modeNames: [axiumName, axiumName], - methodSubscribers: [] as NamedSubscriber[], - generalSubscribers: [] as NamedSubscriber[], + methodSubscribers: [] as NamedSubscription[], + generalSubscribers: [] as NamedSubscription[], + stagePlanners: [] as NamedStagePlanner[], action$: new Subject(), concepts$: new UnifiedSubject(), addConceptQue: [] as Concept[], @@ -94,6 +97,7 @@ export const createAxiumConcept = (name: string, storeDialog?: boolean, logging? clearDialogQuality, logQuality, registerSubscriberQuality, + registerStagePlannerQuality, initializePrinciplesQuality, setBlockingModeQuality, setDefaultModeQuality, diff --git a/src/concepts/axium/axium.mode.ts b/src/concepts/axium/axium.mode.ts index cc1b1c5..ef461f1 100644 --- a/src/concepts/axium/axium.mode.ts +++ b/src/concepts/axium/axium.mode.ts @@ -6,7 +6,7 @@ import { axiumBadActionType } from './qualities/badAction.quality'; import { Concept } from '../../model/concept.js'; import { axiumSetBlockingModeType } from './qualities/setBlockingMode.quality'; import { axiumConcludeType } from './qualities/conclude.quality'; -import { UnifiedSubject } from '../../model/unifiedSubject'; +import { UnifiedSubject } from '../../model/stagePlanner'; export const isActionable = (axiumState: AxiumState, action: Action): boolean => { let actionable = true; diff --git a/src/concepts/axium/axium.principle.ts b/src/concepts/axium/axium.principle.ts index 30708f7..6cde0a8 100644 --- a/src/concepts/axium/axium.principle.ts +++ b/src/concepts/axium/axium.principle.ts @@ -1,15 +1,13 @@ import { Subject, Subscriber } from 'rxjs'; import { Concept, Mode } from '../../model/concept'; -import { PrincipleFunction, createPrinciple$ } from '../../model/principle'; -import { Action, createAction, createCacheSemaphores } from '../../model/action'; +import { PrincipleFunction, registerPrincipleSubscription } from '../../model/principle'; +import { Action, createCacheSemaphores } from '../../model/action'; import { AxiumState, axiumName } from './axium.concept'; -import { RegisterSubscriberPayload, axiumRegisterSubscriberType } from './qualities/registerSubscriber.quality'; -import { primeAction } from '../../model/action'; import { strategyBegin } from '../../model/actionStrategy'; import { addConceptsFromQueThenUnblockStrategy } from './strategies/addConcept.strategy'; import { removeConceptsViaQueThenUnblockStrategy } from './strategies/removeConcept.strategy'; import { blockingMode, permissiveMode } from './axium.mode'; -import { UnifiedSubject } from '../../model/unifiedSubject'; +import { UnifiedSubject } from '../../model/stagePlanner'; export const axiumPrinciple: PrincipleFunction = ( observer: Subscriber, @@ -18,7 +16,7 @@ export const axiumPrinciple: PrincipleFunction = ( ) => { let allowAdd = true; let allowRemove = true; - const subscriber = concepts$.subscribe(_concepts => { + const subscription = concepts$.subscribe(_concepts => { const axiumState = _concepts[0].state as AxiumState; if (axiumState.addConceptQue.length === 0) { allowAdd = true; @@ -97,7 +95,5 @@ export const axiumPrinciple: PrincipleFunction = ( )); } }); - const primedRegisterSubscriber = primeAction(concepts, createAction(axiumRegisterSubscriberType)); - primedRegisterSubscriber.payload = { subscriber, name: axiumName } as RegisterSubscriberPayload; - observer.next(primedRegisterSubscriber); + registerPrincipleSubscription(observer, concepts, axiumName, subscription); }; diff --git a/src/concepts/axium/qualities/addConceptsFromQue.quality.ts b/src/concepts/axium/qualities/addConceptsFromQue.quality.ts index 7d351d9..5b4b9d3 100644 --- a/src/concepts/axium/qualities/addConceptsFromQue.quality.ts +++ b/src/concepts/axium/qualities/addConceptsFromQue.quality.ts @@ -26,7 +26,7 @@ function addConceptsFromQueReducer(state: AxiumState, _ : Action) { const action$ = state.action$ as Subject; blockingMethodSubscription(action$, action); }) as Subscriber; - methodSubscribers.push({name: concept.name, subscriber: methodSub}); + methodSubscribers.push({name: concept.name, subscription: methodSub}); } }); }); diff --git a/src/concepts/axium/qualities/close.quality.ts b/src/concepts/axium/qualities/close.quality.ts index a1df698..976b8a6 100644 --- a/src/concepts/axium/qualities/close.quality.ts +++ b/src/concepts/axium/qualities/close.quality.ts @@ -5,20 +5,22 @@ import { AxiumState } from '../axium.concept'; export const axiumCloseType: ActionType = 'Close Axium'; export const axiumClose = prepareActionCreator(axiumCloseType); -export function closeReducer(state: AxiumState, _action: Action) { - state.generalSubscribers.forEach(named => named.subscriber.unsubscribe()); - state.methodSubscribers.forEach(named => named.subscriber.unsubscribe()); +export function closeReducer(state: AxiumState, _action: Action): AxiumState { + state.generalSubscribers.forEach(named => named.subscription.unsubscribe()); + state.methodSubscribers.forEach(named => named.subscription.unsubscribe()); + state.stagePlanners.forEach(named => named.conclude()); state.action$.complete(); state.concepts$.complete(); state.subConcepts$.complete(); return { ...state, methodSubscribers: [], - generalSubscribers: [] + generalSubscribers: [], + stagePlanners: [], }; } export const closeQuality = createQuality( axiumCloseType, closeReducer -); \ No newline at end of file +); diff --git a/src/concepts/axium/qualities/initializePrinciples.quality.ts b/src/concepts/axium/qualities/initializePrinciples.quality.ts index 0618647..60bca88 100644 --- a/src/concepts/axium/qualities/initializePrinciples.quality.ts +++ b/src/concepts/axium/qualities/initializePrinciples.quality.ts @@ -4,7 +4,7 @@ import { createPrinciple$ } from '../../../model/principle'; import { Action, ActionType, prepareActionWithPayloadCreator } from '../../../model/action'; import { AxiumState } from '../axium.concept'; import { createQuality } from '../../../model/concept'; -import { UnifiedSubject } from '../../../model/unifiedSubject'; +import { UnifiedSubject } from '../../../model/stagePlanner'; import { selectPayload } from '../../../model/selector'; export type InitializePrinciplesPayload = { @@ -26,7 +26,7 @@ export function initializePrinciplesReducer(state: AxiumState, _action: Action) const observable = createPrinciple$(principle, concepts, subConcepts$); subscribers.push({ name: concept.name, - subscriber: observable.subscribe((action: Action) => action$.next(action)) as Subscriber, + subscription: observable.subscribe((action: Action) => action$.next(action)) as Subscriber, }); }); } diff --git a/src/concepts/axium/qualities/registerStagePlanner.quality.ts b/src/concepts/axium/qualities/registerStagePlanner.quality.ts new file mode 100644 index 0000000..58e557c --- /dev/null +++ b/src/concepts/axium/qualities/registerStagePlanner.quality.ts @@ -0,0 +1,32 @@ +import { defaultMethodCreator } from '../../../model/concept'; +import { Action, ActionType, prepareActionWithPayloadCreator } from '../../../model/action'; +import { AxiumState } from '../axium.concept'; +import { createQuality } from '../../../model/concept'; +import { selectPayload } from '../../../model/selector'; +import { StagePlanner } from '../../../model/stagePlanner'; + +export type RegisterStagePlannerPayload = { + stagePlanner: StagePlanner; + conceptName: string; +} +export const axiumRegisterStagePlannerType: ActionType = 'register Stage Planner to Axium\'s Named Stage Planner list'; +export const axiumRegisterStagePlanner = + prepareActionWithPayloadCreator(axiumRegisterStagePlannerType); + +export function registerSubscriberReducer(state: AxiumState, action: Action): AxiumState { + const payload = selectPayload(action); + const stagePlanners = state.stagePlanners; + const stagePlanner = payload.stagePlanner; + const name = payload.conceptName; + stagePlanners.push({name, ...stagePlanner}); + return { + ...state, + stagePlanners, + }; +} + +export const registerStagePlannerQuality = createQuality( + axiumRegisterStagePlannerType, + registerSubscriberReducer, + defaultMethodCreator +); diff --git a/src/concepts/axium/qualities/registerSubscriber.quality.ts b/src/concepts/axium/qualities/registerSubscription.quality.ts similarity index 85% rename from src/concepts/axium/qualities/registerSubscriber.quality.ts rename to src/concepts/axium/qualities/registerSubscription.quality.ts index 6db13fb..643ff01 100644 --- a/src/concepts/axium/qualities/registerSubscriber.quality.ts +++ b/src/concepts/axium/qualities/registerSubscription.quality.ts @@ -1,4 +1,4 @@ -import { Subscriber } from 'rxjs'; +import { Subscriber, Subscription } from 'rxjs'; import { defaultMethodCreator } from '../../../model/concept'; import { Action, ActionType, prepareActionWithPayloadCreator } from '../../../model/action'; import { AxiumState } from '../axium.concept'; @@ -6,7 +6,7 @@ import { createQuality } from '../../../model/concept'; import { selectPayload } from '../../../model/selector'; export type RegisterSubscriberPayload = { - subscriber: Subscriber; + subscription: Subscription; name: string; } export const axiumRegisterSubscriberType: ActionType = 'register Subscriber to Axium\'s General Subscriber list'; @@ -16,9 +16,9 @@ export const axiumRegisterSubscriber = export function registerSubscriberReducer(state: AxiumState, action: Action) { const payload = selectPayload(action); const generalSubscribers = state.generalSubscribers; - const subscriber = payload.subscriber; + const subscription = payload.subscription; const name = payload.name; - generalSubscribers.push({name, subscriber}); + generalSubscribers.push({name, subscription}); return { ...state, generalSubscribers, diff --git a/src/concepts/axium/qualities/removeConceptsViaQue.quality.ts b/src/concepts/axium/qualities/removeConceptsViaQue.quality.ts index 3852411..3f5b1ad 100644 --- a/src/concepts/axium/qualities/removeConceptsViaQue.quality.ts +++ b/src/concepts/axium/qualities/removeConceptsViaQue.quality.ts @@ -1,18 +1,20 @@ import { defaultMethodCreator } from '../../../model/concept'; -import { NamedSubscriber } from '../axium.concept'; +import { NamedSubscription } from '../axium.concept'; import { Action, ActionType, prepareActionCreator, } from '../../../model/action'; import { AxiumState } from '../axium.concept'; import { createQuality } from '../../../model/concept'; +import { NamedStagePlanner } from '../../../model/stagePlanner'; export const axiumRemoveConceptsViaQueType: ActionType = 'remove Concepts via Axium\'s Removal Concept Que'; export const axiumRemoveConceptsViaQue = prepareActionCreator(axiumRemoveConceptsViaQueType); export function removeConceptsViaQueReducer(state: AxiumState, _action: Action) { const methodSubscribers = state.methodSubscribers; - const newMethodSubscribers = [] as NamedSubscriber[]; + const newMethodSubscribers = [] as NamedSubscription[]; const generalSubscribers = state.methodSubscribers; - const newGeneralSubscribers = [] as NamedSubscriber[]; - + const newGeneralSubscribers = [] as NamedSubscription[]; + const stagePlanners = state.stagePlanners; + const newStagePlanners = [] as NamedStagePlanner[]; const removeConceptQue = state.removeConceptQue; methodSubscribers.forEach(named => { @@ -25,7 +27,7 @@ export function removeConceptsViaQueReducer(state: AxiumState, _action: Action) if (!exists) { newMethodSubscribers.push(named); } else { - named.subscriber.unsubscribe(); + named.subscription.unsubscribe(); } }); @@ -39,7 +41,21 @@ export function removeConceptsViaQueReducer(state: AxiumState, _action: Action) if (!exists) { newGeneralSubscribers.push(named); } else { - named.subscriber.unsubscribe(); + named.subscription.unsubscribe(); + } + }); + + stagePlanners.forEach(named => { + let exists = false; + removeConceptQue.forEach(concept => { + if (concept.name === named.name) { + exists = true; + } + }); + if (!exists) { + newStagePlanners.push(named); + } else { + named.conclude(); } }); @@ -48,6 +64,7 @@ export function removeConceptsViaQueReducer(state: AxiumState, _action: Action) // generation: state.generation + 1, methodSubscribers: newMethodSubscribers, generalSubscribers: newGeneralSubscribers, + stagePlanners: newStagePlanners, removeConceptQue: [] }; } diff --git a/src/concepts/axium/qualities/setBlockingMode.quality.ts b/src/concepts/axium/qualities/setBlockingMode.quality.ts index 08e6d56..0fb106b 100644 --- a/src/concepts/axium/qualities/setBlockingMode.quality.ts +++ b/src/concepts/axium/qualities/setBlockingMode.quality.ts @@ -14,7 +14,7 @@ export const axiumSetBlockingMode = prepareActionWithPayloadCreator named.subscriber.unsubscribe()); + methodSubscribers.forEach(named => named.subscription.unsubscribe()); methodSubscribers = []; const payload = selectPayload(_action); @@ -28,7 +28,7 @@ export function setBlockingModeReducer(state: AxiumState, _action: Action) { }); methodSubscribers.push({ name: concept.name, - subscriber: sub as Subscriber + subscription: sub as Subscriber }); } }); diff --git a/src/concepts/axium/qualities/setDefaultMode.quality.ts b/src/concepts/axium/qualities/setDefaultMode.quality.ts index 2e5f268..d1740e5 100644 --- a/src/concepts/axium/qualities/setDefaultMode.quality.ts +++ b/src/concepts/axium/qualities/setDefaultMode.quality.ts @@ -14,7 +14,7 @@ export const axiumSetDefaultMode = prepareActionWithPayloadCreator named.subscriber.unsubscribe()); + methodSubscribers.forEach(named => named.subscription.unsubscribe()); methodSubscribers = []; const payload = selectPayload(_action); const concepts = payload.concepts; @@ -27,7 +27,7 @@ export function setDefaultModeReducer(state: AxiumState, _action: Action) { }); methodSubscribers.push({ name: concept.name, - subscriber: sub as Subscriber + subscription: sub as Subscriber }); } }); diff --git a/src/concepts/chain/chain.principle.ts b/src/concepts/chain/chain.principle.ts index 3340a15..c73ee8f 100644 --- a/src/concepts/chain/chain.principle.ts +++ b/src/concepts/chain/chain.principle.ts @@ -1,24 +1,17 @@ import { Subscriber, Subject } from 'rxjs'; import { Concept } from '../../model/concept'; -import { Action, createAction, primeAction } from '../../model/action'; -import { PrincipleFunction } from '../../model/principle'; +import { Action } from '../../model/action'; +import { PrincipleFunction, registerPrincipleSubscription } from '../../model/principle'; import { Chain, chainName } from './chain.concept'; import { selectState } from '../../model/selector'; import { AxiumState } from '../axium/axium.concept'; -import { - RegisterSubscriberPayload, axiumRegisterSubscriberType, -} from '../axium/qualities/registerSubscriber.quality'; export const chainPrinciple: PrincipleFunction = ( observer: Subscriber, _concepts: Concept[], concepts$: Subject, ) => { - // let pass = true; - // setInterval(() => { - // pass = true; - // }, 50); - const subscriber = concepts$.subscribe((concepts: Concept[]) => { + const subscription = concepts$.subscribe((concepts: Concept[]) => { const chainState = selectState(concepts, chainName); if (chainState.actionQue.length > 0) { // pass = false; @@ -30,10 +23,5 @@ export const chainPrinciple: PrincipleFunction = ( axiumState.action$?.next(nextAction); } }); - const primedRegisterSubscriber = primeAction(_concepts, createAction(axiumRegisterSubscriberType)); - primedRegisterSubscriber.payload = { - subscriber, - name: chainName, - } as RegisterSubscriberPayload; - observer.next(primedRegisterSubscriber); + registerPrincipleSubscription(observer, _concepts, chainName, subscription); }; diff --git a/src/concepts/experiment/experiment.principle.ts b/src/concepts/experiment/experiment.principle.ts index 5cc94c3..64d3bce 100644 --- a/src/concepts/experiment/experiment.principle.ts +++ b/src/concepts/experiment/experiment.principle.ts @@ -1,11 +1,12 @@ import { Subscriber } from 'rxjs'; -import { Action } from '../../model/action'; -import { PrincipleFunction, registerPrincipleSubscription } from '../../model/principle'; +import { Action, primeAction } from '../../model/action'; +import { PrincipleFunction } from '../../model/principle'; import { Concept } from '../../model/concept'; -import { UnifiedSubject } from '../../model/unifiedSubject'; +import { UnifiedSubject } from '../../model/stagePlanner'; import { selectState } from '../../model/selector'; -import { AxiumState, axiumName } from '../axium/axium.concept'; import { ExperimentActionQueState, experimentName } from './experiment.concept'; +import { axiumRegisterStagePlanner } from '../axium/qualities/registerStagePlanner.quality'; +import { axiumSelectOpen } from '../axium/axium.selector'; export const experimentActionQuePrinciple: PrincipleFunction = ( observer: Subscriber, @@ -13,35 +14,39 @@ export const experimentActionQuePrinciple: PrincipleFunction = ( concepts$: UnifiedSubject ) => { let readyToGo = false; - const sub = concepts$.subscribe(_cpts => { - const axiumState = selectState(_cpts, axiumName); - if (axiumState.open) { - const subscription = concepts$.subscribe(cpts => { - const concepts = cpts; - const experimentState = selectState(concepts, experimentName); - // console.log('Check que', experimentState.actionQue); - if (experimentState.actionQue.length > 0) { - if (!readyToGo) { - readyToGo = true; - setTimeout(() => { - readyToGo = false; - const nextAction = experimentState.actionQue.shift(); - // console.log('Dispatched from Experiment Principle', nextAction, experimentState.actionQue); - if (nextAction) { - experimentState.actionQue = [... experimentState.actionQue]; - concepts$.next(concepts); - observer.next(nextAction); - } else { - experimentState.actionQue = []; - concepts$.next(concepts); - } - }, 400); - } - } + const plan = concepts$.stage('ownership Principle Plan', [ + (concepts, dispatch) => { + dispatch(primeAction(concepts, axiumRegisterStagePlanner({conceptName: experimentName, stagePlanner: plan})), { + on: { + selector: axiumSelectOpen, + expected: true, + }, + iterateStage: true }); - sub.unsubscribe(); - registerPrincipleSubscription(observer, _cpts, experimentName, subscription); + }, + (cpts, _) => { + const concepts = cpts; + const experimentState = selectState(concepts, experimentName); + // console.log('Check que', experimentState.actionQue); + if (experimentState.actionQue.length > 0) { + if (!readyToGo) { + readyToGo = true; + setTimeout(() => { + readyToGo = false; + const nextAction = experimentState.actionQue.shift(); + // console.log('Dispatched from Experiment Principle', nextAction, experimentState.actionQue); + if (nextAction) { + experimentState.actionQue = [... experimentState.actionQue]; + concepts$.next(concepts); + observer.next(nextAction); + } else { + experimentState.actionQue = []; + concepts$.next(concepts); + } + }, 400); + } + } } - }); + ]); }; diff --git a/src/concepts/ownership/ownership.mode.ts b/src/concepts/ownership/ownership.mode.ts index 5f7eb1f..112cf44 100644 --- a/src/concepts/ownership/ownership.mode.ts +++ b/src/concepts/ownership/ownership.mode.ts @@ -5,7 +5,7 @@ import { Mode } from '../../model/concept'; import { permissiveMode, blockingMode } from '../axium/axium.mode'; import { checkIn, clearStubs, ownershipShouldBlock, updateAddToPendingActions } from '../../model/ownership'; import { ActionStrategy, strategyFailed } from '../../model/actionStrategy'; -import { UnifiedSubject } from '../../model/unifiedSubject'; +import { UnifiedSubject } from '../../model/stagePlanner'; import { AppendActionListToDialogPayload, axiumAppendActionListToDialogType } from '../axium/qualities/appendActionListToDialog.quality'; import { selectState } from '../../model/selector'; import { OwnershipState, ownershipName } from './ownership.concept'; diff --git a/src/concepts/ownership/ownership.principle.ts b/src/concepts/ownership/ownership.principle.ts index 7dd5dee..b368035 100644 --- a/src/concepts/ownership/ownership.principle.ts +++ b/src/concepts/ownership/ownership.principle.ts @@ -3,14 +3,14 @@ import { Concept } from '../../model/concept'; import { PrincipleFunction } from '../../model/principle'; import { OwnershipState, ownershipName} from '../ownership/ownership.concept'; import { setOwnershipModeStrategy } from './strategies/setOwnerShipMode.strategy'; -import { AxiumState, axiumName } from '../axium/axium.concept'; -import { Action, areSemaphoresEqual, createAction } from '../../model/action'; +import { Action, areSemaphoresEqual, createAction, primeAction } from '../../model/action'; import { selectState } from '../../model/selector'; import { strategyBegin } from '../../model/actionStrategy'; -import { registerPrincipleSubscription } from '../../model/principle'; import { OwnershipTicket, createOwnershipLedger, isActionReady } from '../../model/ownership'; -import { UnifiedSubject } from '../../model/unifiedSubject'; +import { UnifiedSubject } from '../../model/stagePlanner'; import { BadActionPayload, axiumBadActionType } from '../axium/qualities/badAction.quality'; +import { axiumRegisterStagePlanner } from '../axium/qualities/registerStagePlanner.quality'; +import { axiumSelectOpen } from '../axium/axium.selector'; export const ownershipPrinciple: PrincipleFunction = ( observer: Subscriber, @@ -19,114 +19,122 @@ export const ownershipPrinciple: PrincipleFunction = ( ) => { let initDispatch = false; let finalCheck = true; - const sub = concepts$.subscribe(_cpts => { - const axiumState = selectState(_cpts, axiumName); - if (axiumState.open) { - const subscription = concepts$.subscribe(cpts => { - let concepts = cpts; - let ownershipState = selectState(concepts, ownershipName); - if (ownershipState.initialized) { - // This will be the point of dispatch of Qued Actions - let newAction; - if (ownershipState.pendingActions.length > 0) { - // One Action at a Time - for (const action of ownershipState.pendingActions) { - let readyToGo = false; - [concepts, readyToGo] = isActionReady(concepts, action); - if (readyToGo) { - newAction = action; - break; + const plan = concepts$.stage('ownership Principle Plan', [ + (concepts, dispatch) => { + dispatch(primeAction(concepts, axiumRegisterStagePlanner({conceptName: ownershipName, stagePlanner: plan})), { + on: { + selector: axiumSelectOpen, + expected: true, + }, + iterateStage: true + }); + }, + (cpts, _) => { + let concepts = cpts; + let ownershipState = selectState(concepts, ownershipName); + if (ownershipState.initialized) { + // This will be the point of dispatch of Qued Actions + let newAction; + if (ownershipState.pendingActions.length > 0) { + // One Action at a Time + for (const action of ownershipState.pendingActions) { + let readyToGo = false; + [concepts, readyToGo] = isActionReady(concepts, action); + if (readyToGo) { + newAction = action; + break; + } + } + if (newAction) { + ownershipState = selectState(concepts, ownershipName); + const newPendingActions = []; + for (const pending of ownershipState.pendingActions) { + if (!areSemaphoresEqual(pending, newAction) && pending.expiration !== newAction.expiration) { + newPendingActions.push(pending); + } else if (areSemaphoresEqual(pending, newAction) && pending.expiration !== newAction.expiration) { + newPendingActions.push(pending); } } - if (newAction) { - ownershipState = selectState(concepts, ownershipName); - const newPendingActions = []; - for (const pending of ownershipState.pendingActions) { - if (!areSemaphoresEqual(pending, newAction) && pending.expiration !== newAction.expiration) { - newPendingActions.push(pending); - } else if (areSemaphoresEqual(pending, newAction) && pending.expiration !== newAction.expiration) { - newPendingActions.push(pending); - } + ownershipState.pendingActions = [...newPendingActions]; + concepts$.next(concepts); + observer.next(newAction); + } else if (!newAction && ownershipState.pendingActions.length !== 0) { + const badActions: BadActionPayload = []; + const newPending: Action[] = []; + for (const pending of ownershipState.pendingActions) { + if (pending.expiration < Date.now()) { + badActions.push(pending); + } else { + newPending.push(pending); } - ownershipState.pendingActions = [...newPendingActions]; + } + if (badActions.length > 0) { + newAction = createAction(axiumBadActionType, badActions); + ownershipState.pendingActions = newPending; concepts$.next(concepts); observer.next(newAction); - } else if (!newAction && ownershipState.pendingActions.length !== 0) { - const badActions: BadActionPayload = []; - const newPending: Action[] = []; - for (const pending of ownershipState.pendingActions) { - if (pending.expiration < Date.now()) { - badActions.push(pending); - } else { - newPending.push(pending); - } - } - if (badActions.length > 0) { - newAction = createAction(axiumBadActionType, badActions); - ownershipState.pendingActions = newPending; + } else if (finalCheck) { + finalCheck = false; + setTimeout(() => { + finalCheck = true; concepts$.next(concepts); - observer.next(newAction); - } else if (finalCheck) { - finalCheck = false; - setTimeout(() => { - finalCheck = true; - concepts$.next(concepts); - }, 200); - } + }, 200); } } - } else if (!initDispatch && !ownershipState.initialized && ownershipState.isResponsibleForMode) { - initDispatch = true; - observer.next( - strategyBegin( - setOwnershipModeStrategy(concepts, 'Ownership') - ) - ); } - }); - sub.unsubscribe(); - registerPrincipleSubscription(observer, _cpts, ownershipName, subscription); + } else if (!initDispatch && !ownershipState.initialized && ownershipState.isResponsibleForMode) { + initDispatch = true; + observer.next( + strategyBegin( + setOwnershipModeStrategy(concepts, 'Ownership') + ) + ); + } } - }); + ]); }; export const ownershipExpirationPrinciple: PrincipleFunction = ( - observer: Subscriber, + _: Subscriber, _concepts: Concept[], concepts$: UnifiedSubject ) => { - const sub = concepts$.subscribe(_cpts => { - const axiumState = selectState(_cpts, axiumName); - if (axiumState.open) { - const subscription = concepts$.subscribe(cpts => { - const concepts = cpts; - const ownershipState = selectState(concepts, ownershipName); - if (ownershipState.initialized) { - let modified = false; - const newLedger = createOwnershipLedger(); - for (const [key, line] of ownershipState.ownershipLedger.entries()) { - const expiredTickets: OwnershipTicket[] = []; - const newLine: OwnershipTicket[] = []; - for (const ticket of line) { - if (ticket.expiration > Date.now()) { - expiredTickets.push(ticket); - } else { - newLine.push(ticket); - } - } - if (expiredTickets.length > 0 && newLine.length > 0) { - modified = true; - newLedger.set(key, newLine); + const plan = concepts$.stage('ownership Principle Plan', [ + (concepts, dispatch) => { + dispatch(primeAction(concepts, axiumRegisterStagePlanner({conceptName: ownershipName, stagePlanner: plan})), { + on: { + selector: axiumSelectOpen, + expected: true, + }, + iterateStage: true + }); + }, + (cpts, __) => { + const concepts = cpts; + const ownershipState = selectState(concepts, ownershipName); + if (ownershipState.initialized) { + let modified = false; + const newLedger = createOwnershipLedger(); + for (const [key, line] of ownershipState.ownershipLedger.entries()) { + const expiredTickets: OwnershipTicket[] = []; + const newLine: OwnershipTicket[] = []; + for (const ticket of line) { + if (ticket.expiration > Date.now()) { + expiredTickets.push(ticket); + } else { + newLine.push(ticket); } } - if (modified) { - ownershipState.ownershipLedger = newLedger; - concepts$.next(concepts); + if (expiredTickets.length > 0 && newLine.length > 0) { + modified = true; + newLedger.set(key, newLine); } } - }); - sub.unsubscribe(); - registerPrincipleSubscription(observer, _cpts, ownershipName, subscription); + if (modified) { + ownershipState.ownershipLedger = newLedger; + concepts$.next(concepts); + } + } } - }); + ]); }; diff --git a/src/concepts/ownership/ownership.selector.ts b/src/concepts/ownership/ownership.selector.ts index bb522f6..10bc593 100644 --- a/src/concepts/ownership/ownership.selector.ts +++ b/src/concepts/ownership/ownership.selector.ts @@ -1,6 +1,11 @@ import { KeyedSelector } from '../../model/selector'; -export const selectOwnershipLedger: KeyedSelector = { +export const ownershipSelectLedger: KeyedSelector = { conceptName: 'ownership', stateKeys: 'ownershipLedger' }; + +export const ownershipSelectInitialized: KeyedSelector = { + conceptName: 'ownership', + stateKeys: 'initialized' +}; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index bbc2843..915fc50 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,7 +27,8 @@ export type { export type { KeyedSelector } from './model/selector'; export { selectState, selectConcept, selectSlice, selectPayload } from './model/selector'; export { PrincipleFunction } from './model/principle'; -export type { dispatchOptions, Staging, UnifiedSubject } from './model/unifiedSubject'; +export type { dispatchOptions, Staging, UnifiedSubject, StagePlanner, NamedStagePlanner } from './model/stagePlanner'; +export { stageWaitForOpenThenIterate, stageWaitForOwnershipThenIterate } from './model/stagePlanner'; export type { OwnershipTicket, OwnershipTicketStub, OwnershipLedger } from './model/ownership'; //** Concept Exports */ @@ -46,6 +47,16 @@ export { axiumSetDefaultModeIndexType, SetDefaultModeIndexPayload } from './concepts/axium/qualities/setDefaultModeIndex.quality'; +export { + axiumRegisterSubscriber, + RegisterSubscriberPayload, + axiumRegisterSubscriberType +} from './concepts/axium/qualities/registerSubscription.quality'; +export { + axiumRegisterStagePlanner, + RegisterStagePlannerPayload, + axiumRegisterStagePlannerType +} from './concepts/axium/qualities/registerStagePlanner.quality'; export { axiumClearDialog, axiumClearDialogType } from './concepts/axium/qualities/clearDialog.quality'; export { axiumSetDefaultMode, axiumSetDefaultModeType, SetDefaultModePayload } from './concepts/axium/qualities/setDefaultMode.quality'; export { axiumSetBlockingMode, axiumSetBlockingModeType, SetBlockingModePayload } from './concepts/axium/qualities/setBlockingMode.quality'; @@ -86,7 +97,7 @@ export { // Ownership export { OwnershipState, ownershipName, createOwnershipConcept } from './concepts/ownership/ownership.concept'; export { ownershipMode } from './concepts/ownership/ownership.mode'; -export { selectOwnershipLedger } from './concepts/ownership/ownership.selector'; +export { ownershipSelectInitialized, ownershipSelectLedger } from './concepts/ownership/ownership.selector'; // Qualities export { ownershipBackTrack, ownershipBackTrackType } from './concepts/ownership/qualities/backTrack.quality'; export { diff --git a/src/model/axium.ts b/src/model/axium.ts index 2b93456..85a22d7 100644 --- a/src/model/axium.ts +++ b/src/model/axium.ts @@ -16,9 +16,7 @@ import { import { axiumBadActionType } from '../concepts/axium/qualities/badAction.quality'; import { axiumCloseType } from '../concepts/axium/qualities/close.quality'; import { - AppendActionListToDialogPayload, axiumAppendActionListToDialog, - axiumAppendActionListToDialogType } from '../concepts/axium/qualities/appendActionListToDialog.quality'; export const blockingMethodSubscription = (action$: Subject, action: Action) => { @@ -90,7 +88,7 @@ export function createAxium(name: string, initialConcepts: Concept[], logging?: axiumState = concepts[0].state as AxiumState; axiumState.methodSubscribers.push({ name: concept.name, - subscriber: methodSub, + subscription: methodSub, }); } }); diff --git a/src/model/concept.ts b/src/model/concept.ts index 6217e9e..9b8b11b 100644 --- a/src/model/concept.ts +++ b/src/model/concept.ts @@ -5,7 +5,7 @@ import { strategySuccess } from './actionStrategy'; import { map } from 'rxjs'; import { KeyedSelector } from './selector'; import { axiumConclude } from '../concepts/axium/qualities/conclude.quality'; -import { UnifiedSubject } from './unifiedSubject'; +import { UnifiedSubject } from './stagePlanner'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Reducer = (state: any, action: Action) => any; diff --git a/src/model/principle.ts b/src/model/principle.ts index 89c2a81..67db748 100644 --- a/src/model/principle.ts +++ b/src/model/principle.ts @@ -1,8 +1,8 @@ import { Observable, Subscriber, Subscription } from 'rxjs'; import { Concept } from './concept'; -import { Action, createAction, primeAction } from './action'; -import { RegisterSubscriberPayload, axiumRegisterSubscriberType } from '../concepts/axium/qualities/registerSubscriber.quality'; -import { UnifiedSubject } from './unifiedSubject'; +import { Action, primeAction } from './action'; +import { axiumRegisterSubscriber } from '../concepts/axium/qualities/registerSubscription.quality'; +import { UnifiedSubject } from './stagePlanner'; export type PrincipleFunction = ( observer: Subscriber, @@ -20,8 +20,7 @@ export function createPrinciple$( }); } -export function registerPrincipleSubscription(observer: Subscriber, concepts: Concept[], name: string, subscriber: Subscription) { - const primedRegisterSubscriber = primeAction(concepts, createAction(axiumRegisterSubscriberType)); - primedRegisterSubscriber.payload = { subscriber, name } as RegisterSubscriberPayload; +export function registerPrincipleSubscription(observer: Subscriber, concepts: Concept[], name: string, subscription: Subscription) { + const primedRegisterSubscriber = primeAction(concepts, axiumRegisterSubscriber({ subscription, name })); observer.next(primedRegisterSubscriber); } \ No newline at end of file diff --git a/src/model/unifiedSubject.ts b/src/model/stagePlanner.ts similarity index 86% rename from src/model/unifiedSubject.ts rename to src/model/stagePlanner.ts index 7855c94..589f0ed 100644 --- a/src/model/unifiedSubject.ts +++ b/src/model/stagePlanner.ts @@ -4,6 +4,8 @@ import { Concept } from './concept'; import { AxiumState } from '../concepts/axium/axium.concept'; import { KeyedSelector, selectSlice } from './selector'; import { Action, ActionType } from './action'; +import { axiumSelectOpen } from '../concepts/axium/axium.selector'; +import { ownershipSelectInitialized } from '../concepts/ownership/ownership.selector'; export type Plan = { title: string; @@ -12,6 +14,19 @@ export type Plan = { stageFailed: number; } +export type NamedStagePlanner = { + name: string; + title: string; + planId: number; + conclude: () => void; +} + +export type StagePlanner = { + title: string; + planId: number; + conclude: () => void; +} + export type dispatchOptions = { runOnce?: boolean; iterateStage?: boolean; @@ -36,6 +51,26 @@ export type StageDelimiter = { runOnceMap: Map } +export const stageWaitForOpenThenIterate = (action: Action): Staging => (concepts: Concept[], dispatch: Dispatcher) => { + dispatch(action, { + on: { + selector: axiumSelectOpen, + expected: true + }, + iterateStage: true + }); +}; + +export const stageWaitForOwnershipThenIterate = (action: Action): Staging => (concepts: Concept[], dispatch: Dispatcher) => { + dispatch(action, { + on: { + selector: ownershipSelectInitialized, + expected: true + }, + iterateStage: true + }); +}; + const handleRun = (value: Concept[], stageDelimiter: StageDelimiter, plan: Plan, action: Action, options?: dispatchOptions) : [StageDelimiter, boolean] => { @@ -133,20 +168,22 @@ const handleStageDelimiter = }; export class UnifiedSubject extends Subject { - private stageId = 0; + private planId = 0; private currentStages: Map = new Map(); private stageDelimiters: Map = new Map(); constructor() { super(); } - stage(title: string, stages: Staging[]) { - this.currentStages.set(this.stageId, {title, stages, stage: 0, stageFailed: -1}); - const stageId = this.stageId; - this.stageId++; + stage(title: string, stages: Staging[]): StagePlanner { + this.currentStages.set(this.planId, {title, stages, stage: 0, stageFailed: -1}); + const planId = this.planId; + this.planId++; const conclude = () => { - this.currentStages.delete(stageId); + this.currentStages.delete(planId); }; return { + title: title, + planId: planId, conclude: conclude.bind(this) }; } diff --git a/src/test/stagedPrinciple.test.ts b/src/test/stagedPrinciple.test.ts index 6321c1f..abe7b4f 100644 --- a/src/test/stagedPrinciple.test.ts +++ b/src/test/stagedPrinciple.test.ts @@ -5,7 +5,7 @@ import { PrincipleFunction } from '../model/principle'; import { Action, ActionType, prepareActionCreator } from '../model/action'; import { Subscriber } from 'rxjs'; import { Concept, createQuality } from '../model/concept'; -import { UnifiedSubject } from '../model/unifiedSubject'; +import { UnifiedSubject } from '../model/stagePlanner'; import { axiumSelectOpen } from '../concepts/axium/axium.selector'; type ExperimentState = {