diff --git a/ActionStrategy.md b/ActionStrategy.md index b03cb20..b9d489a 100644 --- a/ActionStrategy.md +++ b/ActionStrategy.md @@ -182,7 +182,16 @@ export const createAsyncMethodThrottle = export const createAsyncMethodThrottleWithState = (asyncMethodWithState: (controller: ActionController, action: Action, concepts: Concepts) => void, concepts$: UnifiedSubject, semaphore: number, duration: number): [Method, Subject] => {} - +export const createMethodBuffer = + (method: (action: Action) => Action, duration: number): [Method, Subject] => {} +export const createMethodBufferWithState = + (methodWithState: (action: Action, concepts: Concepts) => Action, concepts$: UnifiedSubject, semaphore: number, duration: number) + : [Method, Subject] => {} +export const createAsyncMethodBuffer = + (asyncMethod: (controller: ActionController, action: Action) => void, duration: number): [Method, Subject] => {} +export const createAsyncMethodBufferWithState = + (asyncMethodWithState: (controller: ActionController, action: Action, concepts: Concepts) => + void, concepts$: UnifiedSubject, semaphore: number, duration: number): [Method, Subject] => {} ``` * createMethod - Your standard method, be sure to handle the action.strategy via one of the strategy decision functions, in addition to passing the action if there is no attached strategy. * createMethodWithState - This will allow your method to have the most recent state to be accessed via the asyncMethod function. @@ -198,6 +207,7 @@ export const createAsyncMethodThrottleWithState = * createMethodThrottleWithState- Fires the first action, alongside the most recent state, then filters rest as conclude. * createAsyncMethodThrottle - Asynchronously fires the first action, will filtering the rest for the set duration as conclude. * createAsyncMethodThrottleWithState - Fires the first action asynchronously with the most recent state, and filters action during the duration as conclude to remove stale tickers from ownership if loaded. +* **Buffer Series** similar to debounce method series, but will issue each possible action that encounters the quality for a length of time. Note these will fail ActionStrategies whose time has expired. ## "Creator Functions" Note here this is merely a guideline to inform the creation of your strategies. diff --git a/README.md b/README.md index d6999ed..b15b0f7 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,9 @@ When in doubt simplify. * [Unified Turing Machine](https://github.com/Phuire-Research/Stratimux/blob/main/The-Unified-Turing-Machine.md) - The governing concept for this entire framework. ## Change Log ![Tests](https://github.com/Phuire-Research/Stratimux/actions/workflows/node.js.yml/badge.svg) +### Consistency Update v0.1.70 5/16/2024 +* Added new buffer method series that will delay the dispatch of some possible set of actions for a period of time. +* Finally removed the need to add "as Subject | UnifiedSubject" when creating methods that access state or concepts. ### v0.1.69 5/15/2024 * Added priority to axium strategies. * Improved consistency of logic due the above change. diff --git a/package.json b/package.json index 0d5beba..f7c3afd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stratimux", "license": "GPL-3.0", - "version": "0.1.69", + "version": "0.1.70", "description": "Unified Turing Machine", "main": "dist/index.js", "module": "dist/index.mjs", diff --git a/src/concepts/counter/counter.concept.ts b/src/concepts/counter/counter.concept.ts index 3eb3003..cd2f5c0 100644 --- a/src/concepts/counter/counter.concept.ts +++ b/src/concepts/counter/counter.concept.ts @@ -6,6 +6,7 @@ import { counterAddQuality } from './qualities/add.quality'; import { counterSubtractQuality } from './qualities/subtract.quality'; import { counterSetCountQuality } from './qualities/setCount.quality'; import { createConcept } from '../../model/concept'; +import { counterMultiplyQuality } from './qualities/multiply.quality'; export { countingStrategy, primedCountingStrategy } from './strategies/counting.strategy'; export type CounterState = { @@ -25,7 +26,8 @@ export const createCounterConcept = () => { [ counterAddQuality, counterSubtractQuality, - counterSetCountQuality + counterSetCountQuality, + counterMultiplyQuality ] ); }; diff --git a/src/concepts/counter/qualities/multiply.quality.ts b/src/concepts/counter/qualities/multiply.quality.ts new file mode 100644 index 0000000..4a072bf --- /dev/null +++ b/src/concepts/counter/qualities/multiply.quality.ts @@ -0,0 +1,32 @@ +/*<$ +For the asynchronous graph programming framework Stratimux and Counter Concept, generate a quality that will multiply another by an incoming payload +$>*/ +/*<#*/ +import { defaultMethodCreator } from '../../../model/concept'; +import { CounterState } from '../counter.concept'; +import { counterSelectCount } from '../counter.selector'; +import { createQualitySetWithPayload } from '../../../model/quality'; +import { selectPayload } from '../../../model/selector'; + +type CounterMultiplyPayload = { + by: number; +}; + +export const [ + counterMultiply, + counterMultiplyType, + counterMultiplyQuality +] = createQualitySetWithPayload({ + type: 'Counter Multiply', + reducer: (state: CounterState, action) => { + const {by} = selectPayload(action); + console.log(state.count, 'by', by); + return { + ...state, + count: state.count * by + }; + }, + methodCreator: defaultMethodCreator, + keyedSelectors: [counterSelectCount] +}); +/*#>*/ \ No newline at end of file diff --git a/src/concepts/ownership/ownership.mode.ts b/src/concepts/ownership/ownership.mode.ts index 9d0b772..c66fa07 100644 --- a/src/concepts/ownership/ownership.mode.ts +++ b/src/concepts/ownership/ownership.mode.ts @@ -65,7 +65,6 @@ export const ownershipMode: Mode = ( finalMode([nextAction, concepts, action$, concepts$]); } else { // This assumes that the Strategy is accounting for the Block - // console.log('Check Action Failed1', action); [concepts, action] = checkIn(concepts, action); const nextAction = strategyFailed( strategy, diff --git a/src/concepts/ownership/ownership.principle.ts b/src/concepts/ownership/ownership.principle.ts index 8def159..eb7a84d 100644 --- a/src/concepts/ownership/ownership.principle.ts +++ b/src/concepts/ownership/ownership.principle.ts @@ -40,7 +40,6 @@ export const ownershipPrinciple: PrincipleFunction = ( const plan = concepts$.plan('ownership Principle Plan', [ stageWaitForOpenThenIterate(() => axiumRegisterStagePlanner({conceptName: ownershipName, stagePlanner: plan})), createStage((cpts, _) => { - console.log('HIT'); let concepts = cpts; let ownershipState = selectUnifiedState(concepts, semaphore); if (ownershipState?.initialized) { diff --git a/src/index.ts b/src/index.ts index e9d56db..9903df1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -55,6 +55,12 @@ export { createMethodDebounceWithConcepts, createAsyncMethodThrottleWithConcepts, createAsyncMethodDebounceWithConcepts, + createMethodBuffer, + createMethodBufferWithState, + createMethodBufferWithConcepts, + createAsyncMethodBuffer, + createAsyncMethodBufferWithConcepts, + createAsyncMethodBufferWithState, method } from './model/method'; export { diff --git a/src/model/actionController.ts b/src/model/actionController.ts index 7b84cd6..faca88d 100644 --- a/src/model/actionController.ts +++ b/src/model/actionController.ts @@ -83,6 +83,43 @@ export const createActionController$ = (act: Action, controlling: (controller: A return ctrl; }; +export class ActionControllerForEach extends Subject { + constructor(actions: Action[]) { + super(); + setTimeout(() => { + actions.forEach(action => { + if (action.expiration < Date.now()) { + this.fire(action); + } else if (action.strategy) { + this.fire( + strategyFailed(action.strategy, + strategyData_appendFailure(action.strategy, failureConditions.axiumExpired))); + } else { + this.fire(action); + } + }); + this.complete(); + }, 0); + } + // next(action: Action[]) { + + // } + fire(action: Action) { + if (!this.closed) { + const { observers } = this; + const len = observers.length; + for (let i = 0; i < len; i++) { + observers[i].next(action); + } + } + } +} + +export const createActionControllerForEach$ = (acts: Action[]) => { + const ctrl = new ActionControllerForEach(acts); + return ctrl; +}; + export const actionController = ({ create$: createActionController$ }); diff --git a/src/model/axium.ts b/src/model/axium.ts index 1d4f869..ade399f 100644 --- a/src/model/axium.ts +++ b/src/model/axium.ts @@ -182,7 +182,6 @@ export function createAxium( .subscribe(([action, _concepts]: [Action, Concepts]) => { // Would be notifying methods const _axiumState = _concepts[0].state as AxiumState; - // console.log('CHECK QUES', _axiumState.head, _axiumState.body, _axiumState.tail); if (_axiumState.head.length === 0) { _axiumState.head.push(action); if (_axiumState.tailTimer.length > 0) { @@ -193,11 +192,10 @@ export function createAxium( } const modeIndex = _axiumState.modeIndex; if (getAxiumState(_concepts).logActionStream) { - console.log('CHECK ACTION STREAM', action.type, action.payload, action.semaphore, action.strategy?.topic); + console.log('ACTION STREAM: ', action.type, action.payload, action.semaphore, action.strategy?.topic); } const modes = _concepts[0].mode as Mode[]; const mode = modes[modeIndex] as Mode; - // console.log('STREAM', action, mode); mode([action, _concepts, _axiumState.action$, _axiumState.concepts$]); _axiumState.head.shift(); if (_axiumState.body.length === 0) { diff --git a/src/model/concept.ts b/src/model/concept.ts index 610a363..c2f75bd 100644 --- a/src/model/concept.ts +++ b/src/model/concept.ts @@ -26,7 +26,7 @@ export type Mode = ([action, concept, action$, concepts$]: [ UnifiedSubject, ]) => void; -export type MethodCreator = (concept$?: Subject, semaphore?: number) => [Method, Subject]; +export type MethodCreator = (concept$: Subject, semaphore: number) => [Method, Subject]; // export type MethodCreator = (concept$?: UnifiedSubject, semaphore?: number) => [Method, Subject]; export type Quality = { @@ -325,7 +325,7 @@ export const areConceptsLoaded = (concepts: Concepts, conceptNames: string[]): b return allExists; }; -export const forEachConcept = (concepts: Concepts, each: (concept: Concept, semaphore?: number) => void) => { +export const forEachConcept = (concepts: Concepts, each: (concept: Concept, semaphore: number) => void) => { const conceptKeys = Object.keys(concepts); for (const i of conceptKeys) { const index = Number(i); diff --git a/src/model/method.ts b/src/model/method.ts index 1a8967c..6841099 100644 --- a/src/model/method.ts +++ b/src/model/method.ts @@ -4,9 +4,9 @@ This file hold a series of helper functions that enable users to quickly create within their own defined qualities. $>*/ /*<#*/ -import { Observable, Subject, map, switchMap, withLatestFrom } from 'rxjs'; +import { Observable, Subject, bufferTime, filter, map, mergeMap, switchMap, withLatestFrom } from 'rxjs'; import { Concepts } from './concept'; -import { ActionController, createActionController$ } from './actionController'; +import { ActionController, createActionController$, createActionControllerForEach$ } from './actionController'; import { ActionStrategy } from './actionStrategy'; import { KeyedSelector, selectUnifiedState } from './selector'; import { debounceAction, throttleAction } from './actionOperators'; @@ -98,6 +98,143 @@ export const createAsyncMethodWithState = defaultMethod.toString = () => ('Async Method with State'); return [defaultMethod, defaultSubject]; }; +export const createMethodBuffer = + (method: (action: Action) => Action, duration: number): [Method, Subject] => { + const defaultSubject = new Subject(); + const defaultMethod: Method = defaultSubject.pipe( + bufferTime(duration), + filter(actions => actions.length > 0), + switchMap(actions => createActionControllerForEach$(actions)), + map((action: Action) => { + // Logically Determined axiumConclude + if (action.semaphore[3] !== 3) { + const methodAction = method(action); + if (methodAction.strategy) { + return [methodAction, true]; + } + const conclude = axiumConclude(); + return [{ + ...action, + ...conclude, + }, false]; + } else { + return [action, true]; + } + }), + ); + defaultMethod.toString = () => ('Debounce Method'); + return [defaultMethod, defaultSubject]; + }; +export const createMethodBufferWithState = + (methodWithState: + (action: Action, state: T) => Action, concepts$: Subject, semaphore: number, duration: number): [Method, Subject] => { + const defaultSubject = new Subject(); + const defaultMethod: Method = defaultSubject.pipe( + bufferTime(duration), + switchMap(actions => createActionControllerForEach$(actions)), + withLatestFrom(concepts$), + map(([act, concepts] : [Action, Concepts]): [Action, T] => ([act, selectUnifiedState(concepts, semaphore) as T])), + map(([act, state] : [Action, T]) => { + // Logically Determined axiumConclude + if (act.semaphore[3] !== 3) { + const methodAction = methodWithState(act, state); + if (methodAction.strategy) { + return [methodAction, true]; + } + const conclude = axiumConclude(); + return [{ + ...act, + ...conclude, + }, true]; + } else { + return [act, true]; + } + }), + ); + defaultMethod.toString = () => ('Buffer Method with State'); + return [defaultMethod, defaultSubject]; + }; +export const createMethodBufferWithConcepts = + ( + methodWithConcepts: (action: Action, concepts: Concepts, semaphore: number) => Action, concepts$: Subject, + semaphore: number, + duration: number + ) : [Method, Subject] => { + const defaultSubject = new Subject(); + const defaultMethod: Method = defaultSubject.pipe( + bufferTime(duration), + switchMap(actions => createActionControllerForEach$(actions)), + withLatestFrom(concepts$), + map(([act, concepts] : [Action, Concepts]) => { + // Logically Determined axiumConclude + if (act.semaphore[3] !== 3) { + const methodAction = methodWithConcepts(act, concepts, semaphore); + if (methodAction.strategy) { + return [methodAction, true]; + } + const conclude = axiumConclude(); + return [{ + ...act, + ...conclude, + }, false]; + } else { + return [act, true]; + } + }), + ); + defaultMethod.toString = () => ('Buffer Method with Concepts'); + return [defaultMethod, defaultSubject]; + }; +export const createAsyncMethodBuffer = + (asyncMethod: (controller: ActionController, action: Action) => void, duration: number): [Method, Subject] => { + const defaultSubject = new Subject(); + const defaultMethod: Method = defaultSubject.pipe( + bufferTime(duration), + switchMap(actions => createActionControllerForEach$(actions)), + mergeMap((act) => { + return createActionController$(act, (controller: ActionController, action: Action) => { + asyncMethod(controller, action); + }); + }), + ); + defaultMethod.toString = () => ('Async Buffer Method'); + return [defaultMethod, defaultSubject]; + }; +export const createAsyncMethodBufferWithState = + (asyncMethodWithState: (controller: ActionController, action: Action, state: T) => + void, concepts$: Subject, semaphore: number, duration: number, ): [Method, Subject] => { + const defaultSubject = new Subject(); + const defaultMethod: Method = defaultSubject.pipe( + bufferTime(duration), + switchMap(actions => createActionControllerForEach$(actions)), + withLatestFrom(concepts$), + map(([act, concepts] : [Action, Concepts]): [Action, T] => ([act, selectUnifiedState(concepts, semaphore) as T])), + mergeMap(([act, state] : [Action, T]) => { + return createActionController$(act, (controller: ActionController, action: Action) => { + asyncMethodWithState(controller, action, state); + }); + }) + ); + defaultMethod.toString = () => ('Async Buffer Method with State'); + return [defaultMethod, defaultSubject]; + }; +export const createAsyncMethodBufferWithConcepts = + (asyncMethodWithConcepts: (controller: ActionController, action: Action, concepts: Concepts, semaphore: number) => + void, concepts$: Subject, semaphore: number, duration: number, ): [Method, Subject] => { + const defaultSubject = new Subject(); + const defaultMethod: Method = defaultSubject.pipe( + bufferTime(duration), + switchMap(actions => createActionControllerForEach$(actions)), + withLatestFrom(concepts$), + mergeMap(([act, concepts] : [Action, Concepts]) => { + return createActionController$(act, (controller: ActionController, action: Action) => { + asyncMethodWithConcepts(controller, action, concepts, semaphore); + }); + }), + ); + defaultMethod.toString = () => ('Async Buffer Method with Concepts'); + return [defaultMethod, defaultSubject]; + }; export const createMethodDebounce = (method: (action: Action) => Action, duration: number): [Method, Subject] => { const defaultSubject = new Subject(); @@ -418,5 +555,11 @@ export const method = ({ createAsyncThrottle: createAsyncMethodThrottle, createAsyncThrottleWithState: createAsyncMethodThrottleWithState, createAsyncThrottleWithConcepts: createAsyncMethodThrottleWithConcepts, + createBuffer: createMethodBuffer, + createBufferWithState: createMethodBufferWithState, + createMethodBufferWithConcepts: createMethodBufferWithConcepts, + createAsyncBuffer: createAsyncMethodBuffer, + createAsyncBufferWithState: createAsyncMethodBufferWithState, + createAsyncBufferWithConcepts: createAsyncMethodBufferWithConcepts }); /*#>*/ \ No newline at end of file diff --git a/src/model/priority.ts b/src/model/priority.ts index c115e69..db9efc4 100644 --- a/src/model/priority.ts +++ b/src/model/priority.ts @@ -8,7 +8,6 @@ import { Action } from './action'; // Is only called if action has priority const fillBucket = (body: Action[], bucket: Action[], action: Action, _added = false) => { - // console.log('FILL BUCKET', body, bucket, action, _added); let added = _added; const drop = body.shift(); if (drop) { @@ -43,7 +42,6 @@ const emptyBucket = (body: Action[], bucket: Action[]) => { export const handlePriority = (axiumState: AxiumState, action: Action) => { const body = axiumState.body; - // console.log('HIT HANDLE PRIORITY', body[0],); if (body[0] && body[0].priority !== undefined) { const bucket: Action[] = []; fillBucket(body, bucket, action); diff --git a/src/model/stagePlanner.ts b/src/model/stagePlanner.ts index a17cebb..1de93da 100644 --- a/src/model/stagePlanner.ts +++ b/src/model/stagePlanner.ts @@ -772,14 +772,12 @@ export class UnifiedSubject extends Subject { }); } } - // console.log('CHECK BLOCKING', blocking, this.ques); const notification = (id: number) => { const ready = notifyIds.get(id); const plan = this.currentPlans.get(id); if (plan && ready !== undefined) { this.nextPlan(plan as Plan, ready); } else if (plan && plan.stages[plan.stage].firstRun) { - // console.log('FIRST RUN: ', plan.title); plan.stages[plan.stage].firstRun = false; this.nextPlan(plan as Plan, []); } @@ -807,7 +805,6 @@ export class UnifiedSubject extends Subject { } next(concepts: Concepts) { - // console.log('NEXT', getAxiumState(concepts).tail); if (!this.closed) { this.handleChange(concepts); // We notify subs last to encourage actions being acted upon observations diff --git a/src/test/bufferMethods/bufferMethods.test.ts b/src/test/bufferMethods/bufferMethods.test.ts new file mode 100644 index 0000000..8c60c64 --- /dev/null +++ b/src/test/bufferMethods/bufferMethods.test.ts @@ -0,0 +1,186 @@ +/*<$ +For the asynchronous graph programming framework Stratimux, generate a tests and demonstrates how buffer methods perform their functionality. +$>*/ +/*<#*/ +import { axiumKick } from '../../concepts/axium/qualities/kick.quality'; +import { CounterState, counterName, createCounterConcept } from '../../concepts/counter/counter.concept'; +import { counterSelectCount } from '../../concepts/counter/counter.selector'; +import { counterAdd } from '../../concepts/counter/qualities/add.quality'; +import { counterSetCount } from '../../concepts/counter/qualities/setCount.quality'; +import { createExperimentConcept, createExperimentState } from '../../concepts/experiment/experiment.concept'; +import { createAxium } from '../../model/axium'; +import { selectState } from '../../model/selector'; +import { createStage, stageWaitForOpenThenIterate } from '../../model/stagePlanner'; +import { + experimentAsyncBufferMultiplyByCountFromConcepts, + experimentAsyncBufferMultiplyByCountFromConceptsQuality +} from './qualities/asyncBufferMultiplyByCountFromConceptsAction.quality'; +import { + experimentBufferMultiplyByCountFromConcepts, + experimentBufferMultiplyByCountFromConceptsQuality +} from './qualities/bufferMultiplyByCountFromConceptsAction.quality'; +import { experimentBufferNextAction, experimentBufferNextActionQuality } from './qualities/bufferSomeAction.quality'; + +test('Buffer method periodic count', (done) => { + const experiment = createExperimentConcept(createExperimentState(), [experimentBufferNextActionQuality]); + const axium = createAxium('Experiment method buffer defer actions', [createCounterConcept(), experiment]); + const plan = axium.plan('Experiment buffer add 4 after 10ms', [ + stageWaitForOpenThenIterate(() => axiumKick()), + createStage((_, dispatch) => { + dispatch(experimentBufferNextAction({ + action: counterAdd() + }), { + iterateStage: true, + }); + }), + createStage((concepts, dispatch) => { + const counterState = selectState(concepts, counterName); + expect(counterState?.count).toBe(0); + dispatch(experimentBufferNextAction({ + action: counterAdd() + }), { + iterateStage: true, + }); + }), + createStage((concepts, dispatch) => { + const counterState = selectState(concepts, counterName); + expect(counterState?.count).toBe(0); + dispatch(experimentBufferNextAction({ + action: counterAdd() + }), { + iterateStage: true, + }); + }), + createStage((concepts, dispatch) => { + const counterState = selectState(concepts, counterName); + expect(counterState?.count).toBe(0); + dispatch(experimentBufferNextAction({ + action: counterAdd() + }), { + iterateStage: true, + }); + }), + createStage((concepts, _dispatch, changes) => { + const counterState = selectState(concepts, counterName); + if (changes.length > 0) { + expect(counterState?.count).toBe(4); + setTimeout(() => { + plan.conclude(); + axium.close(); + done(); + }, 10); + } + }, {selectors: [counterSelectCount], beat: 200}), + createStage(() => { + plan.conclude(); + }) + ]); +}); + +test('Buffer method with concept towards final multiply of count', (done) => { + const experiment = createExperimentConcept(createExperimentState(), [experimentBufferMultiplyByCountFromConceptsQuality]); + const axium = createAxium('Experiment method buffer defer multiply', [createCounterConcept(), experiment]); + const plan = axium.plan('Experiment buffer multiply by 2 from concept state after 10ms', [ + stageWaitForOpenThenIterate(() => axiumKick()), + createStage((_, dispatch) => { + dispatch(counterSetCount({ + newCount: 2 + }), { + iterateStage: true, + }); + }), + createStage((concepts, dispatch) => { + const counterState = selectState(concepts, counterName); + expect(counterState?.count).toBe(2); + dispatch(experimentBufferMultiplyByCountFromConcepts(), { + iterateStage: true, + }); + }), + createStage((concepts, dispatch) => { + const counterState = selectState(concepts, counterName); + expect(counterState?.count).toBe(2); + dispatch(experimentBufferMultiplyByCountFromConcepts(), { + iterateStage: true, + }); + }), + createStage((concepts, dispatch) => { + const counterState = selectState(concepts, counterName); + expect(counterState?.count).toBe(2); + dispatch(experimentBufferMultiplyByCountFromConcepts(), { + iterateStage: true, + }); + }), + createStage((concepts, _dispatch, changes) => { + const counterState = selectState(concepts, counterName); + if (changes.length > 0) { + expect(counterState?.count).toBe(16); + setTimeout(() => { + plan.conclude(); + axium.close(); + done(); + }, 10); + } + }, {selectors: [counterSelectCount], beat: 200}), + createStage(() => { + plan.conclude(); + }) + ]); +}); + +test('Buffer method with concept towards final multiply of count', (done) => { + const experiment = createExperimentConcept(createExperimentState(), [experimentAsyncBufferMultiplyByCountFromConceptsQuality]); + const axium = createAxium('Experiment method buffer defer multiply', [createCounterConcept(), experiment], { + // logActionStream: true + }); + const plan = axium.plan('Experiment buffer multiply by 2 from concept state after 10ms', [ + stageWaitForOpenThenIterate(() => axiumKick()), + createStage((_, dispatch) => { + dispatch(counterSetCount({ + newCount: 2 + }), { + iterateStage: true, + }); + }), + createStage((concepts, dispatch) => { + const counterState = selectState(concepts, counterName); + expect(counterState?.count).toBe(2); + dispatch(experimentAsyncBufferMultiplyByCountFromConcepts(), { + iterateStage: true, + }); + }), + createStage((concepts, dispatch) => { + const counterState = selectState(concepts, counterName); + expect(counterState?.count).toBe(2); + dispatch(experimentAsyncBufferMultiplyByCountFromConcepts(), { + iterateStage: true, + }); + }), + createStage((concepts, dispatch) => { + const counterState = selectState(concepts, counterName); + expect(counterState?.count).toBe(2); + dispatch(experimentAsyncBufferMultiplyByCountFromConcepts(), { + iterateStage: true, + }); + }), + createStage((concepts, _dispatch, changes) => { + const counterState = selectState(concepts, counterName); + console.log('CHECK STATE', counterState); + if (changes.length > 0) { + expect(counterState?.count).toBe(16); + setTimeout(() => { + plan.conclude(); + axium.close(); + done(); + }, 10); + } + }, {selectors: [counterSelectCount], beat: 200}), + createStage(() => { + plan.conclude(); + }) + ]); + // axium.subscribe(c => { + // const s = getAxiumState(c); + // console.log(s.head, s.body, s.tail); + // }); +}); +/*#>*/ \ No newline at end of file diff --git a/src/test/bufferMethods/qualities/asyncBufferMultiplyByCountFromConceptsAction.quality.ts b/src/test/bufferMethods/qualities/asyncBufferMultiplyByCountFromConceptsAction.quality.ts new file mode 100644 index 0000000..9671a8f --- /dev/null +++ b/src/test/bufferMethods/qualities/asyncBufferMultiplyByCountFromConceptsAction.quality.ts @@ -0,0 +1,33 @@ +/*<$ +For the asynchronous graph programming framework Stratimux and Experiment Concept, generate a method that will buffer asynchronously +the dispatch of an action assigned to payload. +$>*/ +/*<#*/ +import { defaultReducer } from '../../../model/concept'; +import { createActionNode, createStrategy, strategyBegin, } from '../../../model/actionStrategy'; +import { createQualitySet, } from '../../../model/quality'; +import { selectState } from '../../../model/selector'; +import { CounterState, counterName } from '../../../concepts/counter/counter.concept'; +import { counterMultiply } from '../../../concepts/counter/qualities/multiply.quality'; +import { createAsyncMethodBufferWithConcepts } from '../../../model/method'; + +export const [ + experimentAsyncBufferMultiplyByCountFromConcepts, + experimentAsyncBufferMultiplyByCountFromConceptsType, + experimentAsyncBufferMultiplyByCountFromConceptsQuality +] = createQualitySet({ + type: 'Experiment will asynchronously buffer multiply count using concepts accessing counter state', + reducer: defaultReducer, + methodCreator: (c, s) => createAsyncMethodBufferWithConcepts((controller, _, concepts) => { + setTimeout(() => { + const counterState = selectState(concepts, counterName); + controller.fire(strategyBegin(createStrategy({ + initialNode: createActionNode(counterMultiply({ + by: counterState?.count as number + })), + topic: 'AsyncBuffered Action Topic' + }))); + }, 50); + }, c, s, 10) +}); +/*#>*/ \ No newline at end of file diff --git a/src/test/bufferMethods/qualities/bufferMultiplyByCountFromConceptsAction.quality.ts b/src/test/bufferMethods/qualities/bufferMultiplyByCountFromConceptsAction.quality.ts new file mode 100644 index 0000000..a31a480 --- /dev/null +++ b/src/test/bufferMethods/qualities/bufferMultiplyByCountFromConceptsAction.quality.ts @@ -0,0 +1,31 @@ +/*<$ +For the asynchronous graph programming framework Stratimux and Experiment Concept, generate a method that will buffer +the dispatch of an action assigned to payload. +$>*/ +/*<#*/ +import { defaultReducer } from '../../../model/concept'; +import { createMethodBufferWithConcepts } from '../../../model/method'; +import { createActionNode, createStrategy, strategyBegin, } from '../../../model/actionStrategy'; +import { createQualitySet } from '../../../model/quality'; +import { selectState } from '../../../model/selector'; +import { CounterState, counterName } from '../../../concepts/counter/counter.concept'; +import { counterMultiply } from '../../../concepts/counter/qualities/multiply.quality'; + +export const [ + experimentBufferMultiplyByCountFromConcepts, + experimentBufferMultiplyByCountFromConceptsType, + experimentBufferMultiplyByCountFromConceptsQuality +] = createQualitySet({ + type: 'Experiment will buffer multiply count using concepts accessing counter state', + reducer: defaultReducer, + methodCreator: (c, s) => createMethodBufferWithConcepts((_, concepts) => { + const counterState = selectState(concepts, counterName); + return strategyBegin(createStrategy({ + initialNode: createActionNode(counterMultiply({ + by: counterState?.count as number + })), + topic: 'Buffered Action Topic' + })); + }, c, s, 10) +}); +/*#>*/ \ No newline at end of file diff --git a/src/test/bufferMethods/qualities/bufferSomeAction.quality.ts b/src/test/bufferMethods/qualities/bufferSomeAction.quality.ts new file mode 100644 index 0000000..f61e68f --- /dev/null +++ b/src/test/bufferMethods/qualities/bufferSomeAction.quality.ts @@ -0,0 +1,32 @@ +/*<$ +For the asynchronous graph programming framework Stratimux and Experiment Concept, generate a method that will buffer +the dispatch of an action assigned to payload. +$>*/ +/*<#*/ +import { defaultReducer } from '../../../model/concept'; +import { createMethodBuffer } from '../../../model/method'; +import { createActionNode, createStrategy, strategyBegin, } from '../../../model/actionStrategy'; +import { createQualitySetWithPayload } from '../../../model/quality'; +import { Action } from '../../../model/action'; +import { selectPayload } from '../../../model/selector'; + +type ExperimentBufferNextActionPayload = { + action: Action +} + +export const [ + experimentBufferNextAction, + experimentBufferNextActionType, + experimentBufferNextActionQuality +] = createQualitySetWithPayload({ + type: 'Experiment will buffer incoming actions for a set duration', + reducer: defaultReducer, + methodCreator: () => createMethodBuffer((action) => { + const act = selectPayload(action).action; + return strategyBegin(createStrategy({ + initialNode: createActionNode(act), + topic: 'Buffered Action Topic' + })); + }, 10) +}); +/*#>*/ \ No newline at end of file diff --git a/src/test/newPlanOptions/qualities/isReady.quality.ts b/src/test/newPlanOptions/qualities/isReady.quality.ts index f9c697b..ac7ccaa 100644 --- a/src/test/newPlanOptions/qualities/isReady.quality.ts +++ b/src/test/newPlanOptions/qualities/isReady.quality.ts @@ -11,7 +11,6 @@ export const experimentPlanOptionsIsReadyType = 'experimentPlanOptions is ready' export const experimentPlanOptionsIsReady = act.prepareActionCreator(experimentPlanOptionsIsReadyType); function experimentPlanOptionsIsReadyReducer(state: ExperimentPlanOptionsState, action: Action): ExperimentPlanOptionsState { - console.log('EXPERIMENT IS READY!!!'); return { ...state, ready: true diff --git a/src/test/priority/qualities/isReady.quality.ts b/src/test/priority/qualities/isReady.quality.ts index 41d7f46..3f2c852 100644 --- a/src/test/priority/qualities/isReady.quality.ts +++ b/src/test/priority/qualities/isReady.quality.ts @@ -11,7 +11,6 @@ export const experimentPriorityIsReadyType = 'experimentPriority is ready'; export const experimentPriorityIsReady = act.prepareActionCreator(experimentPriorityIsReadyType); function experimentPriorityIsReadyReducer(state: ExperimentPriorityState, action: Action): ExperimentPriorityState { - console.log('EXPERIMENT IS READY!!!'); return { ...state, ready: true