diff options
| author | Avi Levkovich <avi@twine-s.com> | 2018-07-12 09:46:44 +0300 |
|---|---|---|
| committer | Avi Levkovich <avi@twine-s.com> | 2018-07-12 09:46:44 +0300 |
| commit | dec3cd03aec51dca67150e2f6ba76c7eda7f11a4 (patch) | |
| tree | 97f8ee8efd166ab9578f1a4baf1b73c45961abb1 /Software | |
| parent | 86b7dbca112046dea1d3dd7d2904440afc2acb7d (diff) | |
| parent | 1fe6f6b50598fff4d52273138936031f3dee7866 (diff) | |
| download | Tango-dec3cd03aec51dca67150e2f6ba76c7eda7f11a4.tar.gz Tango-dec3cd03aec51dca67150e2f6ba76c7eda7f11a4.zip | |
Merge branch 'master' of https://twinetfs.visualstudio.com/_git/Tango
Diffstat (limited to 'Software')
82 files changed, 3150 insertions, 221 deletions
diff --git a/Software/DB/Tango.mdf b/Software/DB/Tango.mdf Binary files differindex 8f365ab17..44805a30c 100644 --- a/Software/DB/Tango.mdf +++ b/Software/DB/Tango.mdf diff --git a/Software/DB/Tango_log.ldf b/Software/DB/Tango_log.ldf Binary files differindex 37ac63858..6bd51c604 100644 --- a/Software/DB/Tango_log.ldf +++ b/Software/DB/Tango_log.ldf diff --git a/Software/Embedded_SW/Embedded/Common/report/report.h b/Software/Embedded_SW/Embedded/Common/report/report.h index b43ed4991..ffe8cbfd4 100644 --- a/Software/Embedded_SW/Embedded/Common/report/report.h +++ b/Software/Embedded_SW/Embedded/Common/report/report.h @@ -556,7 +556,7 @@ extern void ReportSeveritySet(ErrorSeverity level); Report("ERROR :" msg,__FILE__,__LINE__,rc,RpWarning,0,0) #define REPORT_MSG(rc,msg) \ - Report("ERROR :" msg,__FILE__,__LINE__,rc,RpWarning,0,0) + Report("MSG : " msg,__FILE__,__LINE__,rc,RpWarning,0,0) #define ON_ERROR_LOG_ERROR(msg) if ( rc != 0 ) \ Report("ERROR :" msg,__FILE__,__LINE__,rc,RpError,0,0) diff --git a/Software/Embedded_SW/Embedded/Drivers/Uart_Comm/Uart.c b/Software/Embedded_SW/Embedded/Drivers/Uart_Comm/Uart.c index 9d6f50e51..6cd17e5ef 100644 --- a/Software/Embedded_SW/Embedded/Drivers/Uart_Comm/Uart.c +++ b/Software/Embedded_SW/Embedded/Drivers/Uart_Comm/Uart.c @@ -118,6 +118,8 @@ uint32_t Uart_rx_Counter = 0; // //***************************************************************************** //void UART0IntHandler(void) +uint32_t uDataLength[50] = {0}; +byte uindex = 0; void UARTIntHandler(UArg arg0) { uint32_t ui32Status; @@ -143,6 +145,9 @@ void UARTIntHandler(UArg arg0) if (U0_size_bar == 4) { U0_expected_message_size = *(int *)size; + //uDataLength[uindex] = U0_expected_message_size; + //if (uindex++>=50) + // uindex = 0; if (initArray( U0_expected_message_size) == 0) U0_expected_message_size = 0; U0_size_bar = 0; @@ -200,6 +205,9 @@ uint32_t Uart_Tx(char *buffer,size_t length) } } + uDataLength[uindex] = length+4; + if (uindex++>=50) + uindex = 0; // if (ret!=length) // return 1; diff --git a/Software/Embedded_SW/Embedded/Modules/AlarmHandling/Diagnostics.c b/Software/Embedded_SW/Embedded/Modules/AlarmHandling/Diagnostics.c index 08ba6d2b4..94b49a8d9 100644 --- a/Software/Embedded_SW/Embedded/Modules/AlarmHandling/Diagnostics.c +++ b/Software/Embedded_SW/Embedded/Modules/AlarmHandling/Diagnostics.c @@ -276,6 +276,7 @@ uint32_t DiagnosticsStart() DiagnosticsControlId = AddControlCallback(Diagnostics_ControlTrigger,DiagnosticLimit,DiagnosticsEmptyCBFunction,0,0,0); } } + return OK; } uint32_t DiagnosticsStop() { @@ -286,6 +287,7 @@ uint32_t DiagnosticsStop() RemoveControlCallback(DiagnosticsControlId,Diagnostics_ControlTrigger); DiagnosticsControlId = 0xFF; } + return OK; } uint32_t StartDiagnosticsRequestFunc(MessageContainer* requestContainer) { diff --git a/Software/Embedded_SW/Embedded/Modules/Thread/Thread.h b/Software/Embedded_SW/Embedded/Modules/Thread/Thread.h index dad7a1db6..7780287fb 100644 --- a/Software/Embedded_SW/Embedded/Modules/Thread/Thread.h +++ b/Software/Embedded_SW/Embedded/Modules/Thread/Thread.h @@ -48,7 +48,7 @@ extern HardwarePidControlType ThreadMotorIdToControlId[MAX_THREAD_MOTORS_NUM]; extern HardwareMotor MotorsCfg[NUM_OF_MOTORS]; extern HardwarePidControl MotorsControl[MAX_THREAD_MOTORS_NUM]; -extern InternalWinderConfigStruc InternalWinderCfg; +//extern InternalWinderConfigStruc InternalWinderCfg; extern HardwareDancer DancersCfg[MAX_SYSTEM_DANCERS]; #define MAX_CONTROL_SAMPLES 10 diff --git a/Software/Embedded_SW/Embedded/Modules/Thread/Thread_Winder.c b/Software/Embedded_SW/Embedded/Modules/Thread/Thread_Winder.c index a119b0b16..33086b636 100644 --- a/Software/Embedded_SW/Embedded/Modules/Thread/Thread_Winder.c +++ b/Software/Embedded_SW/Embedded/Modules/Thread/Thread_Winder.c @@ -1,7 +1,7 @@ /* * Thread_Winder.c * - * Created on: 25 áîøõ 2018 + * Created on: 25 áîøõ 2018 * Author: shlomo */ #include"include.h" @@ -11,6 +11,8 @@ #include "StateMachines/Printing/PrintingSTM.h" #include "Modules/Control/Control.h" +#include "Common/report/report.h" + #include "drivers/FPGA/FPGA.h" #include "drivers/FPGA/FPGA_SPI_Comm.h" @@ -27,10 +29,35 @@ uint32_t ScrewControlId = 0xFF; uint32_t ScrewNumberOfSteps = 0; //holds the current number of steps for the next screw run - will be used to build the cone uint32_t DirectionChangeCounter = 0; //holds the current number of runs of the screw - will be used to build the cone +InternalWinderConfigStruc InternalWinderCfg = {0}; + uint32_t Winder_Init(void) { return OK; } + + +uint32_t InternalWinderConfigMessage(HardwareWinder* request) +{ + uint32_t status = PASSED; + + InternalWinderCfg.milimetersperrotation = request->millimeterperrotation; + + return status; +} +uint32_t InternalWindingConfigMessage(JobSpool* request) +{ + uint32_t status = PASSED; + + InternalWinderCfg.segmentoffsetpulses = request->segmentoffsetpulses; + InternalWinderCfg.spoolbackingrate = request->backingrate; + InternalWinderCfg.startoffsetpulses = request->startoffsetpulses; + InternalWinderCfg.SpoolBottomBackingRate = request->bottombackingrate; + InternalWinderCfg.NumberOfRotationPerPassage = request->rotationsperpassage; + InternalWinderCfg.diameter = request->diameter; + + return status; +} /* *uint32_t Winder_Prepare(void *JobDetails) @@ -107,7 +134,56 @@ uint32_t ScrewControlId = 0xFF; uint32_t ScrewNumberOfSteps = 0; uint32_t DirectionChangeCounter = 0; */ +/* + InternalWinderCfg.segmentoffsetpulses = request->segmentoffsetpulses; + InternalWinderCfg.spoolbackingrate = request->backingrate; + InternalWinderCfg.startoffsetpulses = request->startoffsetpulses; + InternalWinderCfg.SpoolBottomBackingRate = request->bottombackingrate; + InternalWinderCfg.NumberOfRotationPerPassage = request->rotationsperpassage; + * +Calculate the number of steps. +Initial home position = ... +Initial out movement = ScrewNumberOfSteps +if DirectionChangeCounter %= backingrate : reduce one from the ScrewNumberOfSteps +if DirectionChangeCounter %= bottombackingrate && direction was out: ADD one to the ScrewNumberOfSteps, +// WRONG? if the flag is raised - lower it and reduce one from the ScrewNumberOfSteps +30:100 - 70 +30:99 - 69 +30:98 - 68 +29:98 - 69 +29:97 -68 +InternalWinderCfg.segmentoffsetpulses + int32_t backingrate; + int32_t bottombackingrate; + +numOfSteps = InternalWinderCfg.startoffsetpulses*MotorsCfg[HARDWARE_MOTOR_TYPE__MOTO_SCREW].microstep; +*/ +uint32_t CalculateNumberOfSteps (uint32_t Counter, bool direction) +{ + uint32_t NumberOfSteps = InternalWinderCfg.segmentoffsetpulses*MotorsCfg[HARDWARE_MOTOR_TYPE__MOTO_SCREW].microstep; + if (Counter) + { + if (Counter%InternalWinderCfg.spoolbackingrate == 0) + { + NumberOfSteps -= (Counter/InternalWinderCfg.spoolbackingrate); + REPORT_MSG(ScrewNumberOfSteps, "Head Backing"); + ScrewNumberOfSteps--; + } + if ((Counter%InternalWinderCfg.SpoolBottomBackingRate == 0)||(Counter%InternalWinderCfg.SpoolBottomBackingRate == 0)) + { + if (direction != MotorsCfg[HARDWARE_MOTOR_TYPE__MOTO_SCREW].directionthreadwize) + { + NumberOfSteps += (Counter/InternalWinderCfg.SpoolBottomBackingRate); + ScrewNumberOfSteps++; + REPORT_MSG(ScrewNumberOfSteps, "Bottom Backing"); + } + + } + } + return CalculateNumberOfSteps; + +} uint32_t ScrewDirectionChange(uint32_t deviceID, uint32_t BusyFlag) { if (BusyfirstCall) @@ -118,6 +194,7 @@ uint32_t ScrewDirectionChange(uint32_t deviceID, uint32_t BusyFlag) //ScrewCurrentDirection: false moves out, true moves home if (BusyFlag == NOTBUSY) { + CalculateNumberOfSteps (DirectionChangeCounter++, ScrewCurrentDirection); if (ScrewCurrentDirection == false) { ScrewCurrentDirection = true; @@ -127,7 +204,6 @@ uint32_t ScrewDirectionChange(uint32_t deviceID, uint32_t BusyFlag) ScrewCurrentDirection = false; } MotorMove (HARDWARE_MOTOR_TYPE__MOTO_SCREW,ScrewCurrentDirection,ScrewNumberOfSteps); //process: set point 0, set max speed, move to the specified length, return back. - DirectionChangeCounter++; } /* * calculate new ScrewSpeed and call MotorSetMaxSpeed (HARDWARE_MOTOR_TYPE__MOTO_SCREW,ScrewSpeed); @@ -197,6 +273,7 @@ uint32_t Winder_End(void) void Winder_ScrewHomeLimitSwitchInterrupt(void) { uint32_t status; + //handle glitch - send information to the next time that the motor stops if (Winder_ScrewHoming) { MotorStop(HARDWARE_MOTOR_TYPE__MOTO_SCREW,Hard_Hiz); //stop ASAP @@ -205,6 +282,7 @@ void Winder_ScrewHomeLimitSwitchInterrupt(void) } void Winder_ScrewOutLimitSwitchInterrupt(void) { + //handle glitch - send information to the next time that the motor stops uint32_t status; status = MotorSetDirection(HARDWARE_MOTOR_TYPE__MOTO_SCREW,1-MotorsCfg[HARDWARE_MOTOR_TYPE__MOTO_SCREW].directionthreadwize);//make sure to move the cart out } diff --git a/Software/Embedded_SW/Embedded/Modules/Thread/Thread_init.c b/Software/Embedded_SW/Embedded/Modules/Thread/Thread_init.c index 3c9adbc31..c5f45da78 100644 --- a/Software/Embedded_SW/Embedded/Modules/Thread/Thread_init.c +++ b/Software/Embedded_SW/Embedded/Modules/Thread/Thread_init.c @@ -18,35 +18,10 @@ int32_t MotorSamples[MAX_THREAD_MOTORS_NUM][MAX_CONTROL_SAMPLES] = {0}; int MotorSamplePointer[MAX_THREAD_MOTORS_NUM] = {0}; double NormalizedErrorCoEfficient[MAX_THREAD_MOTORS_NUM] = {0}; int DancerStopActivityLimit[MAX_THREAD_MOTORS_NUM] = {0}; -InternalWinderConfigStruc InternalWinderCfg = {0}; HardwareDancer DancersCfg[MAX_SYSTEM_DANCERS] = {0}; HardwarePidControlType ThreadMotorIdToControlId[MAX_THREAD_MOTORS_NUM] = {HARDWARE_PID_CONTROL_TYPE__MotorFeeder,HARDWARE_PID_CONTROL_TYPE__MotorDryer,HARDWARE_PID_CONTROL_TYPE__MotorPooler,HARDWARE_PID_CONTROL_TYPE__MotorWinder,0}; - - -uint32_t InternalWinderConfigMessage(HardwareWinder* request) -{ - uint32_t status = PASSED; - - InternalWinderCfg.milimetersperrotation = request->millimeterperrotation; - - return status; -} -uint32_t InternalWindingConfigMessage(JobSpool* request) -{ - uint32_t status = PASSED; - - InternalWinderCfg.segmentoffsetpulses = request->segmentoffsetpulses; - InternalWinderCfg.spoolbackingrate = request->backingrate; - InternalWinderCfg.startoffsetpulses = request->startoffsetpulses; - InternalWinderCfg.SpoolBottomBackingRate = request->bottombackingrate; - InternalWinderCfg.NumberOfRotationPerPassage = request->rotationsperpassage; - InternalWinderCfg.diameter = request->diameter; - - return status; -} - //******************************************************************************************************************** uint32_t MotorsConfigMessage(HardwareMotor * request) { diff --git a/Software/Embedded_SW/Embedded/Modules/Thread/Thread_print.c b/Software/Embedded_SW/Embedded/Modules/Thread/Thread_print.c index 0701a2aac..d9aeb503a 100644 --- a/Software/Embedded_SW/Embedded/Modules/Thread/Thread_print.c +++ b/Software/Embedded_SW/Embedded/Modules/Thread/Thread_print.c @@ -70,6 +70,8 @@ ProcessedLengthFunc ProcessedLengthFuncPtr = NULL; void ThreadSegmentEnded(void); void ThreadInterSegmentEnded(void); void ThreadDistanceToSpoolEnded(void); +uint32_t ThreadControlCBFunction(uint32_t IfIndex, uint32_t ReadValue); +void SetOriginMotorSpeed(float process_speed); double KeepNormalizedError = 0; bool ThreadControlActive = false; @@ -248,12 +250,37 @@ uint32_t ThreadControlSpeedReadFunction(uint32_t IfIndex, uint32_t ReadValue) } return OK; } -double calculatedError[1000]; -int readValue[1000]; -int calculatedspeed[1000]; +/*double calculatedError[100]; +double eNormalizedError[100]; +int readValue[100]; +int TranslatedreadValue[100]; +int AveragereadValue[100]; +int calculatedspeed[100]; int controlIndex = 0; int32_t KeepReadValue = 0; - +void testDancersControl() +{ + int mm20,mm10,mm5,mm2,mm1; + mm20 = (20*DancerStopActivityLimit[FEEDER_MOTOR])/(DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].maximalmovementmm*2); + mm2 = mm20/10; + mm5 = mm20/4; + mm10 = mm20/2; + mm1 = mm20/20; + ThreadControlActive = true; + SetOriginMotorSpeed(30.0); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint - mm20); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint - mm10); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint - mm5); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint - mm2); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint - mm1); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint + mm1); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint + mm2); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint + mm5); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint + mm10); + ThreadControlCBFunction(IfTypeThread*0x100+FEEDER_MOTOR, DancersCfg[HARDWARE_DANCER_TYPE__RightDancer].zeropoint + mm20); + ThreadControlActive = false; +}*/ uint32_t ThreadControlCBFunction(uint32_t IfIndex, uint32_t ReadValue) { //#define MAX_CONTROL_SAMPLES 6 @@ -270,6 +297,8 @@ uint32_t ThreadControlCBFunction(uint32_t IfIndex, uint32_t ReadValue) double NormalizedError; char Message[60]; + if (ThreadControlActive == false) + return OK; if (IfIndex>>8 != IfTypeThread) { LOG_ERROR (IfIndex, "Wrong Interface type"); @@ -339,18 +368,21 @@ uint32_t ThreadControlCBFunction(uint32_t IfIndex, uint32_t ReadValue) //KeepNormalizedError = NormalizedError; } calculated_speed = (1-MotorControlConfig[index].m_calculatedError)*OriginalMotorSpd_2PPS[index]; - if (index == POOLER_MOTOR) + /*if (index == FEEDER_MOTOR) { if (KeepReadValue != TranslatedReadValue) { + eNormalizedError[controlIndex] = NormalizedError; calculatedError[controlIndex] = MotorControlConfig[index].m_calculatedError; - readValue[controlIndex] = TranslatedReadValue; + readValue[controlIndex] = ReadValue; + TranslatedreadValue[controlIndex] = TranslatedReadValue; + AveragereadValue[controlIndex] = avreageSampleValue; calculatedspeed[controlIndex] = calculated_speed; controlIndex++; - if (controlIndex >= 999) controlIndex = 0; + if (controlIndex >= 99) controlIndex = 0; KeepReadValue = TranslatedReadValue; } - } + }*/ if (abs(calculated_speed-CurrentControlledSpeed[index])>5) { CurrentControlledSpeed[index] = calculated_speed; @@ -482,6 +514,7 @@ bool InitialProcess = false; // else if ((Motor_i == HARDWARE_MOTOR_TYPE__MOTO_WINDER)||(Motor_i == HARDWARE_MOTOR_TYPE__MOTO_LDRIVING)||(Motor_i == HARDWARE_MOTOR_TYPE__MOTO_RDRIVING)) // AddControlCallback(ThreadControlCBFunction, eOneMillisecond,Control_Read_Dancer_Position,(IfTypeThread*0x100+Motor_i),ThreadMotorIdToDancerId[Motor_i],Motor_i); } + //testDancersControl(); PrepareReady(Module_Thread,ModuleDone); //set 3 dancers to the profile positions InitialProcess = true; diff --git a/Software/Embedded_SW/Embedded/StateMachines/Printing/JobSTM.c b/Software/Embedded_SW/Embedded/StateMachines/Printing/JobSTM.c index 56c1b7be7..3384965d7 100644 --- a/Software/Embedded_SW/Embedded/StateMachines/Printing/JobSTM.c +++ b/Software/Embedded_SW/Embedded/StateMachines/Printing/JobSTM.c @@ -28,6 +28,8 @@ #include <ti/sysbios/knl/Clock.h> #include <ti/sysbios/knl/task.h> +#include "Common/report/report.h" + #include "PMR/Printing/JobSegment.pb-c.h" #include "PMR/Printing/JobRequest.pb-c.h" #include "PMR/Printing/JobResponse.pb-c.h" @@ -359,7 +361,8 @@ void JobRequestFunc(MessageContainer* requestContainer) jobStatus.progress = 0.0; jobStatus.has_currentsegmentindex = false; response.status = &jobStatus; - response.has_canceled = false; + response.has_canceled = true; + response.canceled = true; responseContainer = createContainer(MESSAGE_TYPE__JobResponse, JobToken, true, &response, &job_response__pack, &job_response__get_packed_size); container_buffer = malloc(message_container__get_packed_size(&responseContainer)); responseContainer.has_error = true; @@ -381,38 +384,46 @@ void SendJobProgress(double ProcessedLength, int SegmentId, bool done, char *Mes uint32_t status = NOT_SUPPORTED; MessageContainer responseContainer; uint8_t* container_buffer; + + char logmsg[100]; + int len; + static msdid = 0; + len = usnprintf(logmsg, 100, "MSG: Job Progress Length %f, Seg %d Done %d %s",ProcessedLength, SegmentId, done, Message); + + //REPORT_MSG(msdid++,logmsg); + Report(logmsg,__FILE__,__LINE__,msdid,RpWarning,SegmentId, done); + + + JobResponse response = JOB_RESPONSE__INIT; + JobStatus jobStatus = JOB_STATUS__INIT; + + + jobStatus.message = Message; + if (ProcessedLength == 0.0) { - ProcessedLength = previousJobLength; + //ProcessedLength = previousJobLength; + jobStatus.has_progress = false; + jobStatus.progress = 0; + jobStatus.has_currentsegmentindex = false; } else { - previousJobLength = ProcessedLength; + //previousJobLength = ProcessedLength; + jobStatus.has_progress = true; + jobStatus.progress = ProcessedLength; + jobStatus.has_currentsegmentindex = true; + jobStatus.currentsegmentindex = SegmentId; } + response.status = &jobStatus; - - JobResponse response = JOB_RESPONSE__INIT; - JobStatus jobStatus = JOB_STATUS__INIT; if (done == true) { - jobStatus.message = Message; - jobStatus.has_progress = true; - jobStatus.progress = ProcessedLength; - jobStatus.has_currentsegmentindex = false; - response.status = &jobStatus; - response.has_canceled = false; responseContainer = createContainer(MESSAGE_TYPE__JobResponse, JobToken, true, &response, &job_response__pack, &job_response__get_packed_size); container_buffer = malloc(message_container__get_packed_size(&responseContainer)); } else { - jobStatus.message =Message; - jobStatus.has_progress = true; - jobStatus.progress = ProcessedLength; - jobStatus.has_currentsegmentindex = true; - jobStatus.currentsegmentindex = SegmentId; - response.status = &jobStatus; - response.has_canceled = false; responseContainer = createContainer(MESSAGE_TYPE__JobResponse, JobToken, false, &response, &job_response__pack, &job_response__get_packed_size); container_buffer = malloc(message_container__get_packed_size(&responseContainer)); } diff --git a/Software/Graphics/Mobile/sample-dye.png b/Software/Graphics/Mobile/sample-dye.png Binary files differnew file mode 100644 index 000000000..f8989e19a --- /dev/null +++ b/Software/Graphics/Mobile/sample-dye.png diff --git a/Software/Graphics/Preloaders/preloader_rectangles.gif b/Software/Graphics/Preloaders/preloader_rectangles.gif Binary files differnew file mode 100644 index 000000000..5bb37cc50 --- /dev/null +++ b/Software/Graphics/Preloaders/preloader_rectangles.gif diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.ColorLab/ViewModels/MainViewVM.cs b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.ColorLab/ViewModels/MainViewVM.cs index 48d16104a..0e9109b28 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.ColorLab/ViewModels/MainViewVM.cs +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.ColorLab/ViewModels/MainViewVM.cs @@ -13,6 +13,7 @@ using System.Windows.Media; using Tango.BL; using Tango.BL.ColorConversion; using Tango.BL.Entities; +using Tango.BL.Enumerations; using Tango.Core.Commands; using Tango.MachineStudio.Common; using Tango.MachineStudio.Common.Controls; @@ -32,6 +33,33 @@ namespace Tango.MachineStudio.ColorLab.ViewModels #region Properties + private bool _isVolumesOutOfRange; + /// <summary> + /// Gets or sets a value indicating whether the liquid volumes are out of range. + /// </summary> + public bool IsVolumesOutOfRange + { + get { return _isVolumesOutOfRange; } + set + { + if (_isVolumesOutOfRange != value) + { + _isVolumesOutOfRange = value; + RaisePropertyChangedAuto(); + } + } + } + + private bool _isOutOfGamut; + /// <summary> + /// Gets or sets a value indicating whether the source color is out of gamut. + /// </summary> + public bool IsOutOfGamut + { + get { return _isOutOfGamut; } + set { _isOutOfGamut = value; RaisePropertyChangedAuto(); } + } + private ObservableCollection<Machine> _machines; /// <summary> /// Gets or sets the machines. @@ -280,6 +308,8 @@ namespace Tango.MachineStudio.ColorLab.ViewModels var output = TangoColorConverter.GetSuggestions(input); + IsOutOfGamut = output.OutOfGamut; + HiveSuggestions = TangoColorConverter.CreateHiveSuggestions(output); }); } @@ -396,6 +426,17 @@ namespace Tango.MachineStudio.ColorLab.ViewModels try { if (LiquidsCalibrationData == null) return; + + //TODO: This is temporary because of out of range volumes. + if (LiquidVolumes.Where(x => x.IdsPack.IdsPackFormula.Code == IdsPackFormulas.StandardColor.ToInt32()).Sum(x => x.Volume) > 200) + { + IsVolumesOutOfRange = true; + return; + } + else + { + IsVolumesOutOfRange = false; + } ConversionInput input = new ConversionInput(); input.ColorSpace = PMR.ColorLab.ColorSpace.Volume; diff --git a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.ColorLab/Views/MainView.xaml b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.ColorLab/Views/MainView.xaml index 8b62604d6..d8ef015be 100644 --- a/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.ColorLab/Views/MainView.xaml +++ b/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.ColorLab/Views/MainView.xaml @@ -26,6 +26,7 @@ <converters:ColorToComponentsConverter x:Key="ColorToComponentsConverter" /> <converters:NullObjectToBooleanConverter x:Key="NullObjectToBooleanConverter" /> <converters:ObjectToObjectTypeConverter x:Key="ObjectToObjectTypeConverter" /> + <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> </UserControl.Resources> <Grid> @@ -163,6 +164,12 @@ </Grid.RowDefinitions> <StackPanel VerticalAlignment="Center" Grid.Row="1"> + <StackPanel Height="20"> + <StackPanel Orientation="Horizontal" Margin="0 0 0 5" Visibility="{Binding IsOutOfGamut,Converter={StaticResource BooleanToVisibilityConverter}}"> + <materialDesign:PackIcon Kind="Alert" Foreground="#FF6D6D" /> + <TextBlock Margin="5 0 0 0" Foreground="#FF6D6D">Color is out of gamut</TextBlock> + </StackPanel> + </StackPanel> <Border BorderThickness="1" BorderBrush="#545454" Margin="0" Padding="2 2 2 0"> <brushPicker:BrushPicker x:Name="picker" Color="{Binding SourceColor.Color,Mode=TwoWay}" Background="Transparent" BorderThickness="0" Height="150" BrushTypeVisibility="Collapsed"></brushPicker:BrushPicker> </Border> @@ -254,30 +261,39 @@ </Grid> - <ItemsControl ItemsSource="{Binding LiquidVolumes}" VerticalAlignment="Center" MinWidth="420" Grid.Column="4" Grid.Row="3"> - <ItemsControl.ItemsPanel> - <ItemsPanelTemplate> - <StackPanel VerticalAlignment="Center" Orientation="Horizontal" IsItemsHost="True"></StackPanel> - </ItemsPanelTemplate> - </ItemsControl.ItemsPanel> - <ItemsControl.ItemTemplate> - <DataTemplate> - <StackPanel> - <TextBlock HorizontalAlignment="Center" TextAlignment="Center" Text="{Binding Name}" FontSize="10" Margin="0 0 0 5"></TextBlock> - <ContentControl Focusable="False" Style="{StaticResource numberBorder}" Width="60" Height="60" Margin="10 0 0 0"> - <ContentControl.Foreground> - <SolidColorBrush Color="{Binding Color,Converter={StaticResource ColorToIntegerConverter}}"></SolidColorBrush> - </ContentControl.Foreground> - <mahapps:NumericUpDown FontSize="16" FontFamily="{StaticResource digital-7}" HorizontalAlignment="Center" Value="{Binding Volume, Mode=TwoWay}" Background="Transparent" Width="40" StringFormat="0.00" HideUpDownButtons="True" Minimum="0" Maximum="1000" InterceptArrowKeys="True" BorderThickness="0" InterceptMouseWheel="True" HasDecimals="True" HorizontalContentAlignment="Center"> - <!--<mahapps:NumericUpDown.Resources> + <StackPanel Grid.Column="4" Grid.Row="3"> + <StackPanel Height="25" Margin="25 0 0 0"> + <StackPanel Orientation="Horizontal" Margin="0 0 0 0" Visibility="{Binding IsVolumesOutOfRange,Converter={StaticResource BooleanToVisibilityConverter}}"> + <materialDesign:PackIcon Kind="Alert" Foreground="#FF6D6D" /> + <TextBlock Margin="5 0 0 0" Foreground="#FF6D6D">Liquid volumes exceeds the maximum range</TextBlock> + </StackPanel> + </StackPanel> + + <ItemsControl ItemsSource="{Binding LiquidVolumes}" VerticalAlignment="Center" MinWidth="420"> + <ItemsControl.ItemsPanel> + <ItemsPanelTemplate> + <StackPanel VerticalAlignment="Center" Orientation="Horizontal" IsItemsHost="True"></StackPanel> + </ItemsPanelTemplate> + </ItemsControl.ItemsPanel> + <ItemsControl.ItemTemplate> + <DataTemplate> + <StackPanel> + <TextBlock HorizontalAlignment="Center" TextAlignment="Center" Text="{Binding Name}" FontSize="10" Margin="0 0 0 5"></TextBlock> + <ContentControl Focusable="False" Style="{StaticResource numberBorder}" Width="60" Height="60" Margin="10 0 0 0"> + <ContentControl.Foreground> + <SolidColorBrush Color="{Binding Color,Converter={StaticResource ColorToIntegerConverter}}"></SolidColorBrush> + </ContentControl.Foreground> + <mahapps:NumericUpDown FontSize="16" FontFamily="{StaticResource digital-7}" HorizontalAlignment="Center" Value="{Binding Volume, Mode=TwoWay}" Background="Transparent" Width="40" StringFormat="0.00" HideUpDownButtons="True" Minimum="0" Maximum="1000" InterceptArrowKeys="True" BorderThickness="0" InterceptMouseWheel="True" HasDecimals="True" HorizontalContentAlignment="Center"> + <!--<mahapps:NumericUpDown.Resources> <StaticResource ResourceKey="SelectAllTextBoxResource"></StaticResource> </mahapps:NumericUpDown.Resources>--> - </mahapps:NumericUpDown> - </ContentControl> - </StackPanel> - </DataTemplate> - </ItemsControl.ItemTemplate> - </ItemsControl> + </mahapps:NumericUpDown> + </ContentControl> + </StackPanel> + </DataTemplate> + </ItemsControl.ItemTemplate> + </ItemsControl> + </StackPanel> <Image Source="../Images/arrow-long-Left.png" Grid.Column="2" Grid.Row="3" Width="140" Opacity="0.8"></Image> @@ -286,11 +302,14 @@ <Grid> <DockPanel Width="80" HorizontalAlignment="Left"> <TextBlock DockPanel.Dock="Top" FontSize="9" TextAlignment="Center">COMPOSITE</TextBlock> - <Ellipse Height="70" Margin="0 5 0 0" Stroke="#202020"> - <Ellipse.Fill> - <SolidColorBrush Color="{Binding TargetColor.Color}"></SolidColorBrush> - </Ellipse.Fill> - </Ellipse> + <Grid> + <Ellipse Height="70" Margin="0 5 0 0" Stroke="#202020"> + <Ellipse.Fill> + <SolidColorBrush Color="{Binding TargetColor.Color}"></SolidColorBrush> + </Ellipse.Fill> + </Ellipse> + <materialDesign:PackIcon Visibility="{Binding IsVolumesOutOfRange,Converter={StaticResource BooleanToVisibilityConverter}}" Kind="Alert" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" Width="24" Height="24" /> + </Grid> </DockPanel> <StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Height="60"> diff --git a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Resources/BuildDate.txt b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Resources/BuildDate.txt index 00990166f..60d433d99 100644 --- a/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Resources/BuildDate.txt +++ b/Software/Visual_Studio/MachineStudio/Tango.MachineStudio.UI/Resources/BuildDate.txt @@ -1 +1 @@ -Tue 07/10/2018 16:54:33.30 +Wed 07/11/2018 16:02:40.40 diff --git a/Software/Visual_Studio/Native/Tango.ColorLib/Utils/Interp.cpp b/Software/Visual_Studio/Native/Tango.ColorLib/Utils/Interp.cpp index 7346a9898..68b7e0c93 100644 --- a/Software/Visual_Studio/Native/Tango.ColorLib/Utils/Interp.cpp +++ b/Software/Visual_Studio/Native/Tango.ColorLib/Utils/Interp.cpp @@ -5,6 +5,7 @@ #include <iostream> #include <stdio.h> +#define epsTol 0.05 Interp::Interp(void) { } @@ -26,6 +27,15 @@ void Interp::Eval(double InValue, double &OutValue) int errType = 0; ind = -1; + //Check Bounds + if((InValue<m_xValues[0] - epsTol) || (InValue >m_xValues[m_length - 1] + epsTol)) + throw std::exception("Value out of Bounds"); + if ((InValue > m_xValues[0] - epsTol) && (InValue < m_xValues[0])) + InValue = m_xValues[0]; + + if ((InValue > m_xValues[m_length - 1] ) && (InValue < m_xValues[m_length - 1]+ epsTol)) + InValue = m_xValues[m_length - 1]; + for (m = 0; m < m_length - 1; ++m) { if (m_xValues[m] <= InValue && m_xValues[m + 1] >= InValue) diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Controls/JobSummeryViewer.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Controls/JobSummeryViewer.xaml index 550accac3..c7f42f14d 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Controls/JobSummeryViewer.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Controls/JobSummeryViewer.xaml @@ -101,9 +101,13 @@ <Style TargetType="Grid"> <Setter Property="Visibility" Value="Collapsed"></Setter> <Style.Triggers> - <DataTrigger Binding="{Binding JobType}" Value="{x:Static enumerations:JobTypes.Embroidery}"> + <MultiDataTrigger> + <MultiDataTrigger.Conditions> + <Condition Binding="{Binding JobType}" Value="{x:Static enumerations:JobTypes.Embroidery}" /> + <Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=IsActive}" Value="False" /> + </MultiDataTrigger.Conditions> <Setter Property="Visibility" Value="Visible"></Setter> - </DataTrigger> + </MultiDataTrigger> </Style.Triggers> </Style> </Grid.Style> @@ -112,6 +116,5 @@ <Run Text="x"></Run><Run Text="{Binding NumberOfUnits}"></Run> </TextBlock> </Grid> - <TextBlock></TextBlock> </Grid> </UserControl> diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Controls/JobSummeryViewer.xaml.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Controls/JobSummeryViewer.xaml.cs index 2b9d9c7a9..2c03ad452 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Controls/JobSummeryViewer.xaml.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Controls/JobSummeryViewer.xaml.cs @@ -20,7 +20,13 @@ namespace Tango.PPC.Jobs.Controls /// </summary> public partial class JobSummeryViewer : UserControl { - + public bool IsActive + { + get { return (bool)GetValue(IsActiveProperty); } + set { SetValue(IsActiveProperty, value); } + } + public static readonly DependencyProperty IsActiveProperty = + DependencyProperty.Register("IsActive", typeof(bool), typeof(JobSummeryViewer), new PropertyMetadata(false)); public bool DisplayMarkers { diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/JobView/sample-dye.png b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/JobView/sample-dye.png Binary files differnew file mode 100644 index 000000000..f8989e19a --- /dev/null +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Images/JobView/sample-dye.png diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Tango.PPC.Jobs.csproj b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Tango.PPC.Jobs.csproj index 0163b77c2..aaf9fb453 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Tango.PPC.Jobs.csproj +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Tango.PPC.Jobs.csproj @@ -311,5 +311,8 @@ <ItemGroup> <Resource Include="Images\JobProgressView\clock.png" /> </ItemGroup> + <ItemGroup> + <Resource Include="Images\JobView\sample-dye.png" /> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> </Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobProgressViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobProgressViewVM.cs index a111d3be2..6039dafb8 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobProgressViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobProgressViewVM.cs @@ -10,9 +10,10 @@ using Tango.PPC.Common.Navigation; namespace Tango.PPC.Jobs.ViewModels { - public class JobProgressViewVM : PPCViewModel, INavigationResultProvider<Object, Job> + public class JobProgressViewVM : PPCViewModel { private Job _job; + public Job Job { get { return _job; } @@ -32,21 +33,21 @@ namespace Tango.PPC.Jobs.ViewModels } - public void OnNavigationObjectReceived(Job job) + public override void OnApplicationStarted() { - Job = job; - var jobHandler = ApplicationManager.ConnectedMachine.Print(Job, Adapter.ProcessParametersTables.First()); - jobHandler.StatusChanged += JobHandler_StatusChanged; + ApplicationManager.ConnectedMachineChanged += ApplicationManager_ConnectedMachineChanged; } - public object GetNavigationResult() + private void ApplicationManager_ConnectedMachineChanged(object sender, IMachineOperator e) { - return null; + e.PrintingStarted -= PrintingStarted; + e.PrintingStarted += PrintingStarted; } - public override void OnApplicationStarted() + private void PrintingStarted(object sender, JobHandler jobHandler) { - + Job = jobHandler.Job; + jobHandler.StatusChanged += JobHandler_StatusChanged; } private void JobHandler_StatusChanged(object sender, RunningJobStatus e) diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobSummeryViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobSummeryViewVM.cs index a59b0fe27..19e28f6a9 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobSummeryViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobSummeryViewVM.cs @@ -47,7 +47,8 @@ namespace Tango.PPC.Jobs.ViewModels private void StartJob() { - var result = NavigationManager.NavigateForResult<JobsModule, JobProgressView, Object, Job>(Job); + ApplicationManager.ConnectedMachine.Print(Job); + NavigationManager.NavigateTo<JobsModule>(nameof(JobProgressView)); } public override void OnApplicationStarted() diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobViewVM.cs index 6b25d323f..b23e8b8ed 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobViewVM.cs @@ -39,7 +39,6 @@ namespace Tango.PPC.Jobs.ViewModels #region Properties private Job _job; - /// <summary> /// Gets or sets the selected job. /// </summary> @@ -172,6 +171,21 @@ namespace Tango.PPC.Jobs.ViewModels /// </summary> public RelayCommand<BrushStop> TwineCatalogFieldTapCommand { get; set; } + /// <summary> + /// Gets or sets the increase decrease samples to dye command. + /// </summary> + public RelayCommand<String> IncreaseDecreaseSamplesToDyeCommand { get; set; } + + /// <summary> + /// Gets or sets the start sample dye command. + /// </summary> + public RelayCommand StartSampleDyeCommand { get; set; } + + /// <summary> + /// Gets or sets the dye command. + /// </summary> + public RelayCommand DyeCommand { get; set; } + #endregion #region Constructors @@ -205,9 +219,23 @@ namespace Tango.PPC.Jobs.ViewModels SaveJobCommand = new RelayCommand(SaveJob); ReplaceBrushStopCommand = new RelayCommand<BrushStop>(InvokeColorAdjustmentForBrushStop); TwineCatalogFieldTapCommand = new RelayCommand<BrushStop>(InvokeTwineCatalogForBrushStop); + IncreaseDecreaseSamplesToDyeCommand = new RelayCommand<string>((x) => + { + if (x == "+") + { + Job.SampleUnitsOrMeters++; + } + else + { + Job.SampleUnitsOrMeters--; + } + }); _check_gamut_thread = new Thread(CheckGamutThreadMethod); _check_gamut_thread.IsBackground = true; + + StartSampleDyeCommand = new RelayCommand(StartSampleDye); + DyeCommand = new RelayCommand(StartJob, CanStartJob); } #endregion @@ -238,6 +266,19 @@ namespace Tango.PPC.Jobs.ViewModels } } + private void StartJob() + { + ApplicationManager.ConnectedMachine.Print(Job); + NavigationManager.NavigateTo<JobsModule>(nameof(JobProgressView)); + } + + private bool CanStartJob() + { + return + Job != null && + !Job.Segments.SelectMany(x => x.BrushStops).ToList().Exists(x => x.IsOutOfGamut); + } + #endregion #region Segments Management @@ -390,6 +431,37 @@ namespace Tango.PPC.Jobs.ViewModels #endregion + #region Sample Dye + + /// <summary> + /// Starts a sample dye. + /// </summary> + private void StartSampleDye() + { + Job sampleDyeJob = Job.Clone(); + sampleDyeJob.Name = Job.Name + " (sample)"; + + if (Job.JobType == BL.Enumerations.JobTypes.Embroidery) + { + sampleDyeJob.NumberOfUnits = Job.SampleUnitsOrMeters; + } + else + { + sampleDyeJob.NumberOfUnits = 1; + + foreach (var segment in sampleDyeJob.Segments) + { + segment.Length = Job.SampleUnitsOrMeters; + } + } + + ApplicationManager.ConnectedMachine.Print(sampleDyeJob); + + NavigationManager.NavigateTo<JobsModule>(nameof(JobProgressView)); + } + + #endregion + #region Out Of Gamut Check Thread private void CheckGamutThreadMethod() @@ -407,6 +479,11 @@ namespace Tango.PPC.Jobs.ViewModels stop.IsOutOfGamut = TangoColorConverter.IsOutOfGamut(stop); stop.OutOfGamutChecked = true; } + + InvokeUI(() => + { + DyeCommand.RaiseCanExecuteChanged(); + }); } } } @@ -428,6 +505,8 @@ namespace Tango.PPC.Jobs.ViewModels /// </summary> public async override void OnNavigatedTo() { + if (_job_to_load == null) return; + NotificationProvider.SetGlobalBusyMessage("Loading job details..."); _can_navigate_back = false; @@ -467,14 +546,17 @@ namespace Tango.PPC.Jobs.ViewModels { NotificationProvider.ReleaseGlobalBusyMessage(); }); + + _job_to_load = null; } + /// <summary> - /// Called before the navigation system navigates from this object. + /// Called before the navigation system navigates back from this object. /// Return false to abort the navigation. /// </summary> /// <returns></returns> - public async override Task<bool> OnNavigateOutRequest() + public async override Task<bool> OnNavigateBackRequest() { bool result = true; diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobsViewVM.cs b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobsViewVM.cs index 39963201e..8f2ea355f 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobsViewVM.cs +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/ViewModels/JobsViewVM.cs @@ -270,6 +270,9 @@ namespace Tango.PPC.Jobs.ViewModels Job job = new Job(); job.Name = "untitled"; + job.NumberOfHeads = 1; + job.NumberOfUnits = 1; + job.SampleUnitsOrMeters = 1; job.CreationDate = DateTime.UtcNow; job.JobStatus = JobStatuses.Draft; job.JobType = vm.SelectedJobType.Value; diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobProgressView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobProgressView.xaml index 3a4a895dc..28672a920 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobProgressView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobProgressView.xaml @@ -15,7 +15,7 @@ <UserControl.Resources> <converters:JobProgressToPositionConverter x:Key="JobProgressToPositionConverter" /> </UserControl.Resources> - + <Grid> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0 100 0 0"> <TextBlock FontSize="{StaticResource TangoHeaderFontSize}" HorizontalAlignment="Center" Text="{Binding Job.Name,FallbackValue='Job Name'}"></TextBlock> @@ -54,18 +54,25 @@ <StackPanel> <Canvas.Left> <MultiBinding Converter="{StaticResource JobProgressToPositionConverter}"> - <Binding Path="RunningJobStatus.Progress" /> - <Binding Path="RunningJobStatus.TotalProgress" /> + <Binding Path="RunningJobStatus.CurrentUnitProgress" /> + <Binding Path="RunningJobStatus.CurrentUnitTotalProgress" /> <Binding RelativeSource="{RelativeSource AncestorType=Canvas}" Path="ActualWidth" /> </MultiBinding> </Canvas.Left> - <StackPanel Margin="-30 0 0 0"> + <StackPanel Margin="-50 0 0 0"> <TextBlock HorizontalAlignment="Center" FontSize="{StaticResource TangoTitleFontSize}">now dying</TextBlock> <touch:TouchIcon Margin="0 8 0 0" HorizontalAlignment="Center" Icon="Water" Angle="180" Foreground="{StaticResource TangoPrimaryAccentBrush}" Width="40" Height="40" /> </StackPanel> </StackPanel> </Canvas> - <controls:JobSummeryViewer Height="30" Width="600" DisplayMarkers="False" DataContext="{Binding Job}" /> + + <Grid> + <controls:JobSummeryViewer Height="30" Width="600" DisplayMarkers="False" IsActive="True" DataContext="{Binding Job}" /> + + <TextBlock Margin="0 0 -40 0" VerticalAlignment="Center" FontSize="{StaticResource TangoTitleFontSize}" HorizontalAlignment="Right"> + <Run Text="x"></Run><Run Text="{Binding RunningJobStatus.RemainingUnits}"></Run> + </TextBlock> + </Grid> </StackPanel> </Grid> </StackPanel> diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobSummeryView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobSummeryView.xaml index b956d7b6c..377348581 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobSummeryView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobSummeryView.xaml @@ -56,7 +56,7 @@ <TextBlock Margin="40 10 0 0" FontSize="{StaticResource TangoTitleFontSize}">Tap 'DYE' to start the dying process...</TextBlock> </StackPanel> - <controls:JobSummeryViewer Height="50" Margin="-6 0 -6 0" DataContext="{Binding Job}" VerticalAlignment="Bottom" /> + <controls:JobSummeryViewer Height="50" DataContext="{Binding Job}" VerticalAlignment="Bottom" /> </Grid> </DockPanel> </Grid> diff --git a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobView.xaml b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobView.xaml index ce7f7b528..cf1c3fa37 100644 --- a/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobView.xaml +++ b/Software/Visual_Studio/PPC/Modules/Tango.PPC.Jobs/Views/JobView.xaml @@ -538,7 +538,11 @@ </StackPanel> </Border> - <Border Style="{StaticResource TangoTouchBorder}" Margin="0 12 0 120" Padding="0 0 0 40"> + <StackPanel HorizontalAlignment="Center" Margin="20 40 0 40" Orientation="Horizontal" VerticalAlignment="Center" Style="{StaticResource Level1Container}"> + <TextBlock FontWeight="Medium" Margin="20 0 0 0" VerticalAlignment="Center" FontSize="{StaticResource TangoTitleFontSize}">Additional Tools</TextBlock> + </StackPanel> + + <!--<Border Style="{StaticResource TangoTouchBorder}" Margin="0 12 0 0" Padding="0 0 0 40"> <StackPanel> <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Style="{StaticResource Level1Container}"> <Image Source="../Images/JobView/additional-tools.png" Width="39" /> @@ -549,7 +553,7 @@ <DockPanel LastChildFill="False"> <TextBlock Text="Make a sample dye" VerticalAlignment="Center"></TextBlock> - <touch:TouchToggleSlider DockPanel.Dock="Right" Width="75" Style="{StaticResource TouchToggleButtonCheck}" Margin="30 0 0 0" Height="38" HorizontalAlignment="Left" /> + <touch:TouchToggleSlider x:Name="toggle_sample_dye" DockPanel.Dock="Right" Width="75" Style="{StaticResource TouchToggleButtonCheck}" Margin="30 0 0 0" Height="38" HorizontalAlignment="Left" /> </DockPanel> <DockPanel Margin="0 25 0 0" LastChildFill="False"> @@ -558,7 +562,80 @@ </DockPanel> </StackPanel> </StackPanel> - </Border> + </Border>--> + + <touch:TouchExpander Margin="0 0 0 120" Padding="20 15" IsExpanded="True"> + <touch:TouchExpander.Header> + <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0 0 20 0"> + <Image Source="../Images/JobView/sample-dye.png" Width="39" /> + <TextBlock FontWeight="Medium" Margin="20 0 0 0" VerticalAlignment="Center" FontSize="{StaticResource TangoExpanderHeaderFontSize}">Sample Dye</TextBlock> + </StackPanel> + </touch:TouchExpander.Header> + + <StackPanel Margin="60 20 0 0"> + + <ContentControl Content="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext}"> + <ContentControl.Style> + <Style TargetType="ContentControl"> + <Setter Property="ContentTemplate"> + <Setter.Value> + <DataTemplate> + <StackPanel> + <TextBlock> + <Run>Dye 1 or more units in order to get approval.</Run> + <LineBreak/> + <Run>Once approved, you can dye the entire job.</Run> + </TextBlock> + + <DockPanel Margin="0 50 0 0" LastChildFill="True" Width="450" HorizontalAlignment="Left"> + <TextBlock DockPanel.Dock="Left" VerticalAlignment="Center">How many sample units?</TextBlock> + + <DockPanel Margin="50 0 0 0"> + <touch:TouchIconButton Command="{Binding IncreaseDecreaseSamplesToDyeCommand}" CommandParameter="-" DockPanel.Dock="Left" Icon="Minus" Padding="15" Style="{StaticResource TangoHollowButton}" BorderThickness="2" Width="51" Height="51" /> + <touch:TouchIconButton Command="{Binding IncreaseDecreaseSamplesToDyeCommand}" CommandParameter="+" DockPanel.Dock="Right" Icon="Plus" Padding="15" Style="{StaticResource TangoHollowButton}" BorderThickness="2" Width="51" Height="51" /> + <touch:TouchNumericTextBox Value="{Binding Job.SampleUnitsOrMeters}" Minimum="1" Maximum="100" HorizontalContentAlignment="Center" VerticalAlignment="Center" Margin="10 0" Foreground="{StaticResource TangoPrimaryAccentBrush}" FontSize="{StaticResource TangoTitleFontSize}" DockPanel.Dock="Right" KeyboardContainer="{Binding ElementName=Container}" /> + </DockPanel> + </DockPanel> + </StackPanel> + </DataTemplate> + </Setter.Value> + </Setter> + <Style.Triggers> + <DataTrigger Binding="{Binding Job.JobType}" Value="{x:Static enumerations:JobTypes.Sewing}"> + <Setter Property="ContentTemplate"> + <Setter.Value> + <DataTemplate> + <StackPanel> + <TextBlock> + <Run>Dye several meters per segment in order to get approval.</Run> + <LineBreak/> + <Run>Once approved, you can dye the entire job.</Run> + </TextBlock> + + <DockPanel Margin="0 50 0 0" LastChildFill="True" Width="450" HorizontalAlignment="Left"> + <TextBlock DockPanel.Dock="Left" VerticalAlignment="Center">Meters per segment</TextBlock> + + <DockPanel Margin="50 0 0 0"> + <touch:TouchIconButton Command="{Binding IncreaseDecreaseSamplesToDyeCommand}" CommandParameter="-" DockPanel.Dock="Left" Icon="Minus" Padding="15" Style="{StaticResource TangoHollowButton}" BorderThickness="2" Width="51" Height="51" /> + <touch:TouchIconButton Command="{Binding IncreaseDecreaseSamplesToDyeCommand}" CommandParameter="+" DockPanel.Dock="Right" Icon="Plus" Padding="15" Style="{StaticResource TangoHollowButton}" BorderThickness="2" Width="51" Height="51" /> + <touch:TouchNumericTextBox Value="{Binding Job.SampleUnitsOrMeters}" Minimum="1" Maximum="100" HorizontalContentAlignment="Center" VerticalAlignment="Center" Margin="10 0" Foreground="{StaticResource TangoPrimaryAccentBrush}" FontSize="{StaticResource TangoTitleFontSize}" DockPanel.Dock="Right" KeyboardContainer="{Binding ElementName=Container}" /> + </DockPanel> + </DockPanel> + </StackPanel> + </DataTemplate> + </Setter.Value> + </Setter> + </DataTrigger> + </Style.Triggers> + </Style> + </ContentControl.Style> + </ContentControl> + + <touch:TouchButton Margin="0 50 0 15" DockPanel.Dock="Right" Height="54" Padding="0" Width="184" CornerRadius="30" BlurRadius="20" HorizontalAlignment="Right" Command="{Binding StartSampleDyeCommand}"> + START + </touch:TouchButton> + </StackPanel> + </touch:TouchExpander> </StackPanel> </StackPanel> </touch:LightTouchScrollViewer> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/INavigationBlocker.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/INavigationBlocker.cs index ea5173b95..622c9e2d4 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/INavigationBlocker.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/Navigation/INavigationBlocker.cs @@ -17,5 +17,12 @@ namespace Tango.PPC.Common.Navigation /// </summary> /// <returns></returns> Task<bool> OnNavigateOutRequest(); + + /// <summary> + /// Called before the navigation system navigates back from this object. + /// Return false to abort the navigation. + /// </summary> + /// <returns></returns> + Task<bool> OnNavigateBackRequest(); } } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs index fae905b13..12534959e 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.Common/PPCViewModel.cs @@ -139,6 +139,16 @@ namespace Tango.PPC.Common { return Task.FromResult(true); } + + /// <summary> + /// Called before the navigation system navigates back from this object. + /// Return false to abort the navigation. + /// </summary> + /// <returns></returns> + public virtual Task<bool> OnNavigateBackRequest() + { + return Task.FromResult(true); + } } /// <summary> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Images/preloader_rectangles.gif b/Software/Visual_Studio/PPC/Tango.PPC.UI/Images/preloader_rectangles.gif Binary files differnew file mode 100644 index 000000000..5bb37cc50 --- /dev/null +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Images/preloader_rectangles.gif diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Navigation/DefaultNavigationManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/Navigation/DefaultNavigationManager.cs index 448fc0edc..0ede1306f 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Navigation/DefaultNavigationManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Navigation/DefaultNavigationManager.cs @@ -28,6 +28,7 @@ namespace Tango.PPC.UI.Navigation private Object _currentVM; private String _lastFullPath; private bool _preventHistory; + private bool _navigating_back; private Stack<String> _navigationHistory; @@ -139,9 +140,19 @@ namespace Tango.PPC.UI.Navigation if (_currentVM != null && _currentVM is INavigationBlocker) { - if (!await (_currentVM as INavigationBlocker).OnNavigateOutRequest()) + if (_navigating_back) { - return false; + if (!await (_currentVM as INavigationBlocker).OnNavigateBackRequest()) + { + return false; + } + } + else + { + if (!await (_currentVM as INavigationBlocker).OnNavigateOutRequest()) + { + return false; + } } } @@ -260,6 +271,8 @@ namespace Tango.PPC.UI.Navigation /// </summary> public async Task<bool> NavigateBack() { + _navigating_back = true; + String first = _navigationHistory.Pop(); _preventHistory = true; @@ -268,12 +281,14 @@ namespace Tango.PPC.UI.Navigation { RaisePropertyChanged(nameof(CanNavigateBack)); _preventHistory = false; + _navigating_back = false; return true; } else { _navigationHistory.Push(first); _preventHistory = false; + _navigating_back = false; RaisePropertyChanged(nameof(CanNavigateBack)); return false; } diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs b/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs index 4978fa6e0..c67e83fc6 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/PPCApplication/DefaultPPCApplicationManager.cs @@ -194,7 +194,7 @@ namespace Tango.PPC.UI.PPCApplication private async void ConnectToMachine() { Transport.Discovery.UsbCommunicationScanner<ConnectRequest, ConnectResponse> scanner = new Transport.Discovery.UsbCommunicationScanner<ConnectRequest, ConnectResponse>(UsbSerialBaudRates.BR_9600); - var response = await scanner.Scan(new ConnectRequest() { Password = "1234" }, TimeSpan.FromSeconds(60)); + var response = await scanner.Scan(new ConnectRequest() { Password = "1234" }, TimeSpan.FromSeconds(10)); var machine = new MachineOperator(response.Adapter); await machine.Connect(); diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj b/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj index 4f6221144..159484c0b 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Tango.PPC.UI.csproj @@ -224,6 +224,10 @@ <None Include="App.config" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\..\Tango.AnimatedGif\Tango.AnimatedGif.csproj"> + <Project>{d129789c-3096-4d0b-8dd7-fe24a4df4b21}</Project> + <Name>Tango.AnimatedGif</Name> + </ProjectReference> <ProjectReference Include="..\..\Tango.BL\Tango.BL.csproj"> <Project>{f441feee-322a-4943-b566-110e12fd3b72}</Project> <Name>Tango.BL</Name> @@ -325,6 +329,7 @@ <Link>Tango.ColorLib.dll</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> + <Resource Include="Images\preloader_rectangles.gif" /> <Resource Include="Images\logo.png" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/LayoutView.xaml b/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/LayoutView.xaml index 621bfabdb..2f1b7dd59 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/LayoutView.xaml +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/LayoutView.xaml @@ -105,7 +105,7 @@ </touch:TouchHamburgerButton> </Border> <Grid DockPanel.Dock="Right" Margin="0 0 20 0"> - <StackPanel VerticalAlignment="Center"> + <StackPanel VerticalAlignment="Center" > <Image Source="/Images/GlobalStatus/ready-to-dye.png" Width="33" /> <TextBlock Margin="0 10 0 0">Ready to Dye</TextBlock> </StackPanel> @@ -127,12 +127,9 @@ </Style.Triggers> </Style> </commonControls:AsyncAdornerControl.Style> - <Grid> - <StackPanel Orientation="Horizontal"> - <touch:TouchBusyIndicator Width="50" Height="50" IsIndeterminate="{Binding NotificationProvider.IsInGlobalBusyState}" /> - <TextBlock VerticalAlignment="Center" Margin="10 0 0 0" FontSize="20" Text="{Binding NotificationProvider.GlobalBusyMessage}"></TextBlock> - </StackPanel> - </Grid> + <StackPanel VerticalAlignment="Center"> + <touch:TouchGifAnimation Source="/Images/preloader_rectangles.gif" EnableAnimation="{Binding NotificationProvider.IsInGlobalBusyState}" /> + </StackPanel> </commonControls:AsyncAdornerControl> </Grid> </DockPanel> diff --git a/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/LoginView.xaml b/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/LoginView.xaml index 74bb85e7d..027e61d08 100644 --- a/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/LoginView.xaml +++ b/Software/Visual_Studio/PPC/Tango.PPC.UI/Views/LoginView.xaml @@ -29,9 +29,8 @@ </StackPanel> <StackPanel> <controls:AsyncAdornerControl Height="130" Visibility="{Binding IsLoading,Converter={StaticResource BooleanToVisibilityConverter}}"> - <StackPanel> - <touch:TouchBusyIndicator IsIndeterminate="{Binding IsLoading}" Width="80" Height="80" /> - <TextBlock HorizontalAlignment="Center" Margin="0 20 0 0" FontSize="{StaticResource TangoTitleFontSize}">Logging in...</TextBlock> + <StackPanel VerticalAlignment="Center"> + <touch:TouchGifAnimation Source="/Images/preloader_rectangles.gif" EnableAnimation="{Binding IsLoading}" /> </StackPanel> </controls:AsyncAdornerControl> </StackPanel> diff --git a/Software/Visual_Studio/Tango.AnimatedGif/AnimationCache.cs b/Software/Visual_Studio/Tango.AnimatedGif/AnimationCache.cs new file mode 100644 index 000000000..b9eaf189a --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/AnimationCache.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; + +namespace Tango.AnimatedGif +{ + static class AnimationCache + { + private class CacheKey + { + private readonly ImageSource _source; + private readonly RepeatBehavior _repeatBehavior; + + public CacheKey(ImageSource source, RepeatBehavior repeatBehavior) + { + _source = source; + _repeatBehavior = repeatBehavior; + } + + private bool Equals(CacheKey other) + { + return ImageEquals(_source, other._source) + && Equals(_repeatBehavior, other._repeatBehavior); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((CacheKey)obj); + } + + public override int GetHashCode() + { + unchecked + { + return (ImageGetHashCode(_source) * 397) ^ _repeatBehavior.GetHashCode(); + } + } + + private static int ImageGetHashCode(ImageSource image) + { + if (image != null) + { + var uri = GetUri(image); + if (uri != null) + return uri.GetHashCode(); + } + return 0; + } + + private static bool ImageEquals(ImageSource x, ImageSource y) + { + if (Equals(x, y)) + return true; + if ((x == null) != (y == null)) + return false; + // They can't both be null or Equals would have returned true + // and if any is null, the previous would have detected it + // ReSharper disable PossibleNullReferenceException + if (x.GetType() != y.GetType()) + return false; + // ReSharper restore PossibleNullReferenceException + var xUri = GetUri(x); + var yUri = GetUri(y); + return xUri != null && xUri == yUri; + } + + private static Uri GetUri(ImageSource image) + { + var bmp = image as BitmapImage; + if (bmp != null && bmp.UriSource != null) + { + if (bmp.UriSource.IsAbsoluteUri) + return bmp.UriSource; + if (bmp.BaseUri != null) + return new Uri(bmp.BaseUri, bmp.UriSource); + } + var frame = image as BitmapFrame; + if (frame != null) + { + string s = frame.ToString(); + if (s != frame.GetType().FullName) + { + Uri fUri; + if (Uri.TryCreate(s, UriKind.RelativeOrAbsolute, out fUri)) + { + if (fUri.IsAbsoluteUri) + return fUri; + if (frame.BaseUri != null) + return new Uri(frame.BaseUri, fUri); + } + } + } + return null; + } + } + + private static readonly Dictionary<CacheKey, ObjectAnimationUsingKeyFrames> _animationCache = new Dictionary<CacheKey, ObjectAnimationUsingKeyFrames>(); + private static readonly Dictionary<CacheKey, int> _referenceCount = new Dictionary<CacheKey, int>(); + + public static void IncrementReferenceCount(ImageSource source, RepeatBehavior repeatBehavior) + { + var cacheKey = new CacheKey(source, repeatBehavior); + int count; + _referenceCount.TryGetValue(cacheKey, out count); + count++; + _referenceCount[cacheKey] = count; + } + + public static void DecrementReferenceCount(ImageSource source, RepeatBehavior repeatBehavior) + { + var cacheKey = new CacheKey(source, repeatBehavior); + int count; + _referenceCount.TryGetValue(cacheKey, out count); + if (count > 0) + { + count--; + _referenceCount[cacheKey] = count; + } + if (count == 0) + { + _animationCache.Remove(cacheKey); + _referenceCount.Remove(cacheKey); + } + } + + public static void AddAnimation(ImageSource source, RepeatBehavior repeatBehavior, ObjectAnimationUsingKeyFrames animation) + { + var key = new CacheKey(source, repeatBehavior); + _animationCache[key] = animation; + } + + public static void RemoveAnimation(ImageSource source, RepeatBehavior repeatBehavior, ObjectAnimationUsingKeyFrames animation) + { + var key = new CacheKey(source, repeatBehavior); + _animationCache.Remove(key); + } + + public static ObjectAnimationUsingKeyFrames GetAnimation(ImageSource source, RepeatBehavior repeatBehavior) + { + var key = new CacheKey(source, repeatBehavior); + ObjectAnimationUsingKeyFrames animation; + _animationCache.TryGetValue(key, out animation); + return animation; + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifApplicationExtension.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifApplicationExtension.cs new file mode 100644 index 000000000..bbf8a5820 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifApplicationExtension.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; +using System.Text; + +namespace Tango.AnimatedGif.Decoding +{ + // label 0xFF + internal class GifApplicationExtension : GifExtension + { + internal const int ExtensionLabel = 0xFF; + + public int BlockSize { get; private set; } + public string ApplicationIdentifier { get; private set; } + public byte[] AuthenticationCode { get; private set; } + public byte[] Data { get; private set; } + + private GifApplicationExtension() + { + } + + internal override GifBlockKind Kind + { + get { return GifBlockKind.SpecialPurpose; } + } + + internal static GifApplicationExtension ReadApplication(Stream stream) + { + var ext = new GifApplicationExtension(); + ext.Read(stream); + return ext; + } + + private void Read(Stream stream) + { + // Note: at this point, the label (0xFF) has already been read + + byte[] bytes = new byte[12]; + stream.ReadAll(bytes, 0, bytes.Length); + BlockSize = bytes[0]; // should always be 11 + if (BlockSize != 11) + throw GifHelpers.InvalidBlockSizeException("Application Extension", 11, BlockSize); + + ApplicationIdentifier = Encoding.ASCII.GetString(bytes, 1, 8); + byte[] authCode = new byte[3]; + Array.Copy(bytes, 9, authCode, 0, 3); + AuthenticationCode = authCode; + Data = GifHelpers.ReadDataBlocks(stream, false); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifBlock.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifBlock.cs new file mode 100644 index 000000000..331e7bd8f --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifBlock.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.IO; + +namespace Tango.AnimatedGif.Decoding +{ + internal abstract class GifBlock + { + internal static GifBlock ReadBlock(Stream stream, IEnumerable<GifExtension> controlExtensions, bool metadataOnly) + { + int blockId = stream.ReadByte(); + if (blockId < 0) + throw GifHelpers.UnexpectedEndOfStreamException(); + switch (blockId) + { + case GifExtension.ExtensionIntroducer: + return GifExtension.ReadExtension(stream, controlExtensions, metadataOnly); + case GifFrame.ImageSeparator: + return GifFrame.ReadFrame(stream, controlExtensions, metadataOnly); + case GifTrailer.TrailerByte: + return GifTrailer.ReadTrailer(); + default: + throw GifHelpers.UnknownBlockTypeException(blockId); + } + } + + internal abstract GifBlockKind Kind { get; } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifBlockKind.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifBlockKind.cs new file mode 100644 index 000000000..eab7ea2c1 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifBlockKind.cs @@ -0,0 +1,10 @@ +namespace Tango.AnimatedGif.Decoding +{ + internal enum GifBlockKind + { + Control, + GraphicRendering, + SpecialPurpose, + Other + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifColor.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifColor.cs new file mode 100644 index 000000000..8c0461941 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifColor.cs @@ -0,0 +1,25 @@ +namespace Tango.AnimatedGif.Decoding +{ + internal struct GifColor + { + private readonly byte _r; + private readonly byte _g; + private readonly byte _b; + + internal GifColor(byte r, byte g, byte b) + { + _r = r; + _g = g; + _b = b; + } + + public byte R { get { return _r; } } + public byte G { get { return _g; } } + public byte B { get { return _b; } } + + public override string ToString() + { + return string.Format("#{0:x2}{1:x2}{2:x2}", _r, _g, _b); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifCommentExtension.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifCommentExtension.cs new file mode 100644 index 000000000..cde458d0f --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifCommentExtension.cs @@ -0,0 +1,37 @@ +using System.IO; +using System.Text; + +namespace Tango.AnimatedGif.Decoding +{ + internal class GifCommentExtension : GifExtension + { + internal const int ExtensionLabel = 0xFE; + + public string Text { get; private set; } + + private GifCommentExtension() + { + } + + internal override GifBlockKind Kind + { + get { return GifBlockKind.SpecialPurpose; } + } + + internal static GifCommentExtension ReadComment(Stream stream) + { + var comment = new GifCommentExtension(); + comment.Read(stream); + return comment; + } + + private void Read(Stream stream) + { + // Note: at this point, the label (0xFE) has already been read + + var bytes = GifHelpers.ReadDataBlocks(stream, false); + if (bytes != null) + Text = Encoding.ASCII.GetString(bytes); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifDecoderException.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifDecoderException.cs new file mode 100644 index 000000000..7ff8ef6e0 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifDecoderException.cs @@ -0,0 +1,16 @@ +using System; + +namespace Tango.AnimatedGif.Decoding +{ + [Serializable] + internal class GifDecoderException : Exception + { + internal GifDecoderException() { } + internal GifDecoderException(string message) : base(message) { } + internal GifDecoderException(string message, Exception inner) : base(message, inner) { } + protected GifDecoderException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) + : base(info, context) { } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifExtension.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifExtension.cs new file mode 100644 index 000000000..fda8909ec --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifExtension.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.IO; + +namespace Tango.AnimatedGif.Decoding +{ + internal abstract class GifExtension : GifBlock + { + internal const int ExtensionIntroducer = 0x21; + + internal static GifExtension ReadExtension(Stream stream, IEnumerable<GifExtension> controlExtensions, bool metadataOnly) + { + // Note: at this point, the Extension Introducer (0x21) has already been read + + int label = stream.ReadByte(); + if (label < 0) + throw GifHelpers.UnexpectedEndOfStreamException(); + switch (label) + { + case GifGraphicControlExtension.ExtensionLabel: + return GifGraphicControlExtension.ReadGraphicsControl(stream); + case GifCommentExtension.ExtensionLabel: + return GifCommentExtension.ReadComment(stream); + case GifPlainTextExtension.ExtensionLabel: + return GifPlainTextExtension.ReadPlainText(stream, controlExtensions, metadataOnly); + case GifApplicationExtension.ExtensionLabel: + return GifApplicationExtension.ReadApplication(stream); + default: + throw GifHelpers.UnknownExtensionTypeException(label); + } + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifFile.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifFile.cs new file mode 100644 index 000000000..500ce12a8 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifFile.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Tango.AnimatedGif.Decoding +{ + internal class GifFile + { + public GifHeader Header { get; private set; } + public GifColor[] GlobalColorTable { get; set; } + public IList<GifFrame> Frames { get; set; } + public IList<GifExtension> Extensions { get; set; } + public ushort RepeatCount { get; set; } + + private GifFile() + { + } + + internal static GifFile ReadGifFile(Stream stream, bool metadataOnly) + { + var file = new GifFile(); + file.Read(stream, metadataOnly); + return file; + } + + private void Read(Stream stream, bool metadataOnly) + { + Header = GifHeader.ReadHeader(stream); + + if (Header.LogicalScreenDescriptor.HasGlobalColorTable) + { + GlobalColorTable = GifHelpers.ReadColorTable(stream, Header.LogicalScreenDescriptor.GlobalColorTableSize); + } + ReadFrames(stream, metadataOnly); + + var netscapeExtension = + Extensions + .OfType<GifApplicationExtension>() + .FirstOrDefault(GifHelpers.IsNetscapeExtension); + + if (netscapeExtension != null) + RepeatCount = GifHelpers.GetRepeatCount(netscapeExtension); + else + RepeatCount = 1; + } + + private void ReadFrames(Stream stream, bool metadataOnly) + { + List<GifFrame> frames = new List<GifFrame>(); + List<GifExtension> controlExtensions = new List<GifExtension>(); + List<GifExtension> specialExtensions = new List<GifExtension>(); + while (true) + { + var block = GifBlock.ReadBlock(stream, controlExtensions, metadataOnly); + + if (block.Kind == GifBlockKind.GraphicRendering) + controlExtensions = new List<GifExtension>(); + + if (block is GifFrame) + { + frames.Add((GifFrame)block); + } + else if (block is GifExtension) + { + var extension = (GifExtension)block; + switch (extension.Kind) + { + case GifBlockKind.Control: + controlExtensions.Add(extension); + break; + case GifBlockKind.SpecialPurpose: + specialExtensions.Add(extension); + break; + } + } + else if (block is GifTrailer) + { + break; + } + } + + this.Frames = frames.AsReadOnly(); + this.Extensions = specialExtensions.AsReadOnly(); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifFrame.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifFrame.cs new file mode 100644 index 000000000..85740ec87 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifFrame.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Tango.AnimatedGif.Decoding +{ + internal class GifFrame : GifBlock + { + internal const int ImageSeparator = 0x2C; + + public GifImageDescriptor Descriptor { get; private set; } + public GifColor[] LocalColorTable { get; private set; } + public IList<GifExtension> Extensions { get; private set; } + public GifImageData ImageData { get; private set; } + + private GifFrame() + { + } + + internal override GifBlockKind Kind + { + get { return GifBlockKind.GraphicRendering; } + } + + internal static GifFrame ReadFrame(Stream stream, IEnumerable<GifExtension> controlExtensions, bool metadataOnly) + { + var frame = new GifFrame(); + + frame.Read(stream, controlExtensions, metadataOnly); + + return frame; + } + + private void Read(Stream stream, IEnumerable<GifExtension> controlExtensions, bool metadataOnly) + { + // Note: at this point, the Image Separator (0x2C) has already been read + + Descriptor = GifImageDescriptor.ReadImageDescriptor(stream); + if (Descriptor.HasLocalColorTable) + { + LocalColorTable = GifHelpers.ReadColorTable(stream, Descriptor.LocalColorTableSize); + } + ImageData = GifImageData.ReadImageData(stream, metadataOnly); + Extensions = controlExtensions.ToList().AsReadOnly(); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifGraphicControlExtension.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifGraphicControlExtension.cs new file mode 100644 index 000000000..a3782c011 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifGraphicControlExtension.cs @@ -0,0 +1,52 @@ +using System; +using System.IO; + +namespace Tango.AnimatedGif.Decoding +{ + // label 0xF9 + internal class GifGraphicControlExtension : GifExtension + { + internal const int ExtensionLabel = 0xF9; + + public int BlockSize { get; private set; } + public int DisposalMethod { get; private set; } + public bool UserInput { get; private set; } + public bool HasTransparency { get; private set; } + public int Delay { get; private set; } + public int TransparencyIndex { get; private set; } + + private GifGraphicControlExtension() + { + + } + + internal override GifBlockKind Kind + { + get { return GifBlockKind.Control; } + } + + internal static GifGraphicControlExtension ReadGraphicsControl(Stream stream) + { + var ext = new GifGraphicControlExtension(); + ext.Read(stream); + return ext; + } + + private void Read(Stream stream) + { + // Note: at this point, the label (0xF9) has already been read + + byte[] bytes = new byte[6]; + stream.ReadAll(bytes, 0, bytes.Length); + BlockSize = bytes[0]; // should always be 4 + if (BlockSize != 4) + throw GifHelpers.InvalidBlockSizeException("Graphic Control Extension", 4, BlockSize); + byte packedFields = bytes[1]; + DisposalMethod = (packedFields & 0x1C) >> 2; + UserInput = (packedFields & 0x02) != 0; + HasTransparency = (packedFields & 0x01) != 0; + Delay = BitConverter.ToUInt16(bytes, 2) * 10; // milliseconds + TransparencyIndex = bytes[4]; + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifHeader.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifHeader.cs new file mode 100644 index 000000000..b903b83c9 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifHeader.cs @@ -0,0 +1,38 @@ +using System.IO; + +namespace Tango.AnimatedGif.Decoding +{ + internal class GifHeader : GifBlock + { + public string Signature { get; private set; } + public string Version { get; private set; } + public GifLogicalScreenDescriptor LogicalScreenDescriptor { get; private set; } + + private GifHeader() + { + } + + internal override GifBlockKind Kind + { + get { return GifBlockKind.Other; } + } + + internal static GifHeader ReadHeader(Stream stream) + { + var header = new GifHeader(); + header.Read(stream); + return header; + } + + private void Read(Stream stream) + { + Signature = GifHelpers.ReadString(stream, 3); + if (Signature != "GIF") + throw GifHelpers.InvalidSignatureException(Signature); + Version = GifHelpers.ReadString(stream, 3); + if (Version != "87a" && Version != "89a") + throw GifHelpers.UnsupportedVersionException(Version); + LogicalScreenDescriptor = GifLogicalScreenDescriptor.ReadLogicalScreenDescriptor(stream); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifHelpers.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifHelpers.cs new file mode 100644 index 000000000..801b3da27 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifHelpers.cs @@ -0,0 +1,110 @@ +using System; +using System.IO; +using System.Text; + +namespace Tango.AnimatedGif.Decoding +{ + internal static class GifHelpers + { + public static string ReadString(Stream stream, int length) + { + byte[] bytes = new byte[length]; + stream.ReadAll(bytes, 0, length); + return Encoding.ASCII.GetString(bytes); + } + + public static byte[] ReadDataBlocks(Stream stream, bool discard) + { + MemoryStream ms = discard ? null : new MemoryStream(); + using (ms) + { + int len; + while ((len = stream.ReadByte()) > 0) + { + byte[] bytes = new byte[len]; + stream.ReadAll(bytes, 0, len); + if (ms != null) + ms.Write(bytes, 0, len); + } + if (ms != null) + return ms.ToArray(); + return null; + } + } + + public static GifColor[] ReadColorTable(Stream stream, int size) + { + int length = 3 * size; + byte[] bytes = new byte[length]; + stream.ReadAll(bytes, 0, length); + GifColor[] colorTable = new GifColor[size]; + for (int i = 0; i < size; i++) + { + byte r = bytes[3 * i]; + byte g = bytes[3 * i + 1]; + byte b = bytes[3 * i + 2]; + colorTable[i] = new GifColor(r, g, b); + } + return colorTable; + } + + public static bool IsNetscapeExtension(GifApplicationExtension ext) + { + return ext.ApplicationIdentifier == "NETSCAPE" + && Encoding.ASCII.GetString(ext.AuthenticationCode) == "2.0"; + } + + public static ushort GetRepeatCount(GifApplicationExtension ext) + { + if (ext.Data.Length >= 3) + { + return BitConverter.ToUInt16(ext.Data, 1); + } + return 1; + } + + public static Exception UnexpectedEndOfStreamException() + { + return new GifDecoderException("Unexpected end of stream before trailer was encountered"); + } + + public static Exception UnknownBlockTypeException(int blockId) + { + return new GifDecoderException("Unknown block type: 0x" + blockId.ToString("x2")); + } + + public static Exception UnknownExtensionTypeException(int extensionLabel) + { + return new GifDecoderException("Unknown extension type: 0x" + extensionLabel.ToString("x2")); + } + + public static Exception InvalidBlockSizeException(string blockName, int expectedBlockSize, int actualBlockSize) + { + return new GifDecoderException( + string.Format( + "Invalid block size for {0}. Expected {1}, but was {2}", + blockName, + expectedBlockSize, + actualBlockSize)); + } + + public static Exception InvalidSignatureException(string signature) + { + return new GifDecoderException("Invalid file signature: " + signature); + } + + public static Exception UnsupportedVersionException(string version) + { + return new GifDecoderException("Unsupported version: " + version); + } + + public static void ReadAll(this Stream stream, byte[] buffer, int offset, int count) + { + int totalRead = 0; + while (totalRead < count) + { + totalRead += stream.Read(buffer, offset + totalRead, count - totalRead); + } + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifImageData.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifImageData.cs new file mode 100644 index 000000000..32ae1359b --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifImageData.cs @@ -0,0 +1,27 @@ +using System.IO; + +namespace Tango.AnimatedGif.Decoding +{ + internal class GifImageData + { + public byte LzwMinimumCodeSize { get; set; } + public byte[] CompressedData { get; set; } + + private GifImageData() + { + } + + internal static GifImageData ReadImageData(Stream stream, bool metadataOnly) + { + var imgData = new GifImageData(); + imgData.Read(stream, metadataOnly); + return imgData; + } + + private void Read(Stream stream, bool metadataOnly) + { + LzwMinimumCodeSize = (byte)stream.ReadByte(); + CompressedData = GifHelpers.ReadDataBlocks(stream, metadataOnly); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifImageDescriptor.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifImageDescriptor.cs new file mode 100644 index 000000000..ad344ef4e --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifImageDescriptor.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; + +namespace Tango.AnimatedGif.Decoding +{ + internal class GifImageDescriptor + { + public int Left { get; private set; } + public int Top { get; private set; } + public int Width { get; private set; } + public int Height { get; private set; } + public bool HasLocalColorTable { get; private set; } + public bool Interlace { get; private set; } + public bool IsLocalColorTableSorted { get; private set; } + public int LocalColorTableSize { get; private set; } + + private GifImageDescriptor() + { + } + + internal static GifImageDescriptor ReadImageDescriptor(Stream stream) + { + var descriptor = new GifImageDescriptor(); + descriptor.Read(stream); + return descriptor; + } + + private void Read(Stream stream) + { + byte[] bytes = new byte[9]; + stream.ReadAll(bytes, 0, bytes.Length); + Left = BitConverter.ToUInt16(bytes, 0); + Top = BitConverter.ToUInt16(bytes, 2); + Width = BitConverter.ToUInt16(bytes, 4); + Height = BitConverter.ToUInt16(bytes, 6); + byte packedFields = bytes[8]; + HasLocalColorTable = (packedFields & 0x80) != 0; + Interlace = (packedFields & 0x40) != 0; + IsLocalColorTableSorted = (packedFields & 0x20) != 0; + LocalColorTableSize = 1 << ((packedFields & 0x07) + 1); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifLogicalScreenDescriptor.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifLogicalScreenDescriptor.cs new file mode 100644 index 000000000..55bcb860b --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifLogicalScreenDescriptor.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; + +namespace Tango.AnimatedGif.Decoding +{ + internal class GifLogicalScreenDescriptor + { + public int Width { get; private set; } + public int Height { get; private set; } + public bool HasGlobalColorTable { get; private set; } + public int ColorResolution { get; private set; } + public bool IsGlobalColorTableSorted { get; private set; } + public int GlobalColorTableSize { get; private set; } + public int BackgroundColorIndex { get; private set; } + public double PixelAspectRatio { get; private set; } + + internal static GifLogicalScreenDescriptor ReadLogicalScreenDescriptor(Stream stream) + { + var descriptor = new GifLogicalScreenDescriptor(); + descriptor.Read(stream); + return descriptor; + } + + private void Read(Stream stream) + { + byte[] bytes = new byte[7]; + stream.ReadAll(bytes, 0, bytes.Length); + + Width = BitConverter.ToUInt16(bytes, 0); + Height = BitConverter.ToUInt16(bytes, 2); + byte packedFields = bytes[4]; + HasGlobalColorTable = (packedFields & 0x80) != 0; + ColorResolution = ((packedFields & 0x70) >> 4) + 1; + IsGlobalColorTableSorted = (packedFields & 0x08) != 0; + GlobalColorTableSize = 1 << ((packedFields & 0x07) + 1); + BackgroundColorIndex = bytes[5]; + PixelAspectRatio = + bytes[5] == 0 + ? 0.0 + : (15 + bytes[5]) / 64.0; + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifPlainTextExtension.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifPlainTextExtension.cs new file mode 100644 index 000000000..ddd20c7b7 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifPlainTextExtension.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Tango.AnimatedGif.Decoding +{ + // label 0x01 + internal class GifPlainTextExtension : GifExtension + { + internal const int ExtensionLabel = 0x01; + + public int BlockSize { get; private set; } + public int Left { get; private set; } + public int Top { get; private set; } + public int Width { get; private set; } + public int Height { get; private set; } + public int CellWidth { get; private set; } + public int CellHeight { get; private set; } + public int ForegroundColorIndex { get; private set; } + public int BackgroundColorIndex { get; private set; } + public string Text { get; private set; } + + public IList<GifExtension> Extensions { get; private set; } + + private GifPlainTextExtension() + { + } + + internal override GifBlockKind Kind + { + get { return GifBlockKind.GraphicRendering; } + } + + internal static GifPlainTextExtension ReadPlainText(Stream stream, IEnumerable<GifExtension> controlExtensions, bool metadataOnly) + { + var plainText = new GifPlainTextExtension(); + plainText.Read(stream, controlExtensions, metadataOnly); + return plainText; + } + + private void Read(Stream stream, IEnumerable<GifExtension> controlExtensions, bool metadataOnly) + { + // Note: at this point, the label (0x01) has already been read + + byte[] bytes = new byte[13]; + stream.ReadAll(bytes,0, bytes.Length); + + BlockSize = bytes[0]; + if (BlockSize != 12) + throw GifHelpers.InvalidBlockSizeException("Plain Text Extension", 12, BlockSize); + + Left = BitConverter.ToUInt16(bytes, 1); + Top = BitConverter.ToUInt16(bytes, 3); + Width = BitConverter.ToUInt16(bytes, 5); + Height = BitConverter.ToUInt16(bytes, 7); + CellWidth = bytes[9]; + CellHeight = bytes[10]; + ForegroundColorIndex = bytes[11]; + BackgroundColorIndex = bytes[12]; + + var dataBytes = GifHelpers.ReadDataBlocks(stream, metadataOnly); + Text = Encoding.ASCII.GetString(dataBytes); + Extensions = controlExtensions.ToList().AsReadOnly(); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifTrailer.cs b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifTrailer.cs new file mode 100644 index 000000000..82e153ad3 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Decoding/GifTrailer.cs @@ -0,0 +1,21 @@ +namespace Tango.AnimatedGif.Decoding +{ + internal class GifTrailer : GifBlock + { + internal const int TrailerByte = 0x3B; + + private GifTrailer() + { + } + + internal override GifBlockKind Kind + { + get { return GifBlockKind.Other; } + } + + internal static GifTrailer ReadTrailer() + { + return new GifTrailer(); + } + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/ImageAnimationController.cs b/Software/Visual_Studio/Tango.AnimatedGif/ImageAnimationController.cs new file mode 100644 index 000000000..239af9c8b --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/ImageAnimationController.cs @@ -0,0 +1,192 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Windows.Controls; +using System.Windows.Media.Animation; + +namespace Tango.AnimatedGif +{ + /// <summary> + /// Provides a way to pause, resume or seek a GIF animation. + /// </summary> + public class ImageAnimationController : IDisposable + { + private static readonly DependencyPropertyDescriptor _sourceDescriptor; + + static ImageAnimationController() + { + _sourceDescriptor = DependencyPropertyDescriptor.FromProperty(Image.SourceProperty, typeof (Image)); + } + + private readonly Image _image; + private readonly ObjectAnimationUsingKeyFrames _animation; + private readonly AnimationClock _clock; + private readonly ClockController _clockController; + + + public double SpeedRatio + { + get + { + return _clockController.SpeedRatio; + } + set + { + _clockController.SpeedRatio = value; + } + } + + internal ImageAnimationController(Image image, ObjectAnimationUsingKeyFrames animation, bool autoStart) + { + _image = image; + _animation = animation; + _animation.Completed += AnimationCompleted; + _clock = _animation.CreateClock(); + _clockController = _clock.Controller; + _sourceDescriptor.AddValueChanged(image, ImageSourceChanged); + + // ReSharper disable once PossibleNullReferenceException + _clockController.Pause(); + + _image.ApplyAnimationClock(Image.SourceProperty, _clock); + + if (autoStart) + _clockController.Resume(); + } + + void AnimationCompleted(object sender, EventArgs e) + { + _image.RaiseEvent(new System.Windows.RoutedEventArgs(ImageBehavior.AnimationCompletedEvent, _image)); + } + + private void ImageSourceChanged(object sender, EventArgs e) + { + OnCurrentFrameChanged(); + } + + /// <summary> + /// Returns the number of frames in the image. + /// </summary> + public int FrameCount + { + get { return _animation.KeyFrames.Count; } + } + + /// <summary> + /// Returns the duration of the animation. + /// </summary> + public TimeSpan Duration + { + get + { + return _animation.Duration.HasTimeSpan + ? _animation.Duration.TimeSpan + : TimeSpan.Zero; + } + } + + /// <summary> + /// Returns a value that indicates whether the animation is paused. + /// </summary> + public bool IsPaused + { + get { return _clock.IsPaused; } + } + + /// <summary> + /// Returns a value that indicates whether the animation is complete. + /// </summary> + public bool IsComplete + { + get { return _clock.CurrentState == ClockState.Filling; } + } + + /// <summary> + /// Seeks the animation to the specified frame index. + /// </summary> + /// <param name="index">The index of the frame to seek to</param> + public void GotoFrame(int index) + { + var frame = _animation.KeyFrames[index]; + _clockController.Seek(frame.KeyTime.TimeSpan, TimeSeekOrigin.BeginTime); + } + + /// <summary> + /// Returns the current frame index. + /// </summary> + public int CurrentFrame + { + get + { + var time = _clock.CurrentTime; + var frameAndIndex = + _animation.KeyFrames + .Cast<ObjectKeyFrame>() + .Select((f, i) => new { Time = f.KeyTime.TimeSpan, Index = i }) + .FirstOrDefault(fi => fi.Time >= time); + if (frameAndIndex != null) + return frameAndIndex.Index; + return -1; + } + } + + /// <summary> + /// Pauses the animation. + /// </summary> + public void Pause() + { + _clockController.Pause(); + } + + /// <summary> + /// Starts or resumes the animation. If the animation is complete, it restarts from the beginning. + /// </summary> + public void Play() + { + _clockController.Resume(); + } + + /// <summary> + /// Raised when the current frame changes. + /// </summary> + public event EventHandler CurrentFrameChanged; + + private void OnCurrentFrameChanged() + { + EventHandler handler = CurrentFrameChanged; + if (handler != null) handler(this, EventArgs.Empty); + } + + /// <summary> + /// Finalizes the current object. + /// </summary> + ~ImageAnimationController() + { + Dispose(false); + } + + /// <summary> + /// Disposes the current object. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Disposes the current object + /// </summary> + /// <param name="disposing">true to dispose both managed an unmanaged resources, false to dispose only managed resources</param> + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _image.BeginAnimation(Image.SourceProperty, null); + _animation.Completed -= AnimationCompleted; + _sourceDescriptor.RemoveValueChanged(_image, ImageSourceChanged); + _image.Source = null; + } + } + } +}
\ No newline at end of file diff --git a/Software/Visual_Studio/Tango.AnimatedGif/ImageBehavior.cs b/Software/Visual_Studio/Tango.AnimatedGif/ImageBehavior.cs new file mode 100644 index 000000000..a159106e6 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/ImageBehavior.cs @@ -0,0 +1,966 @@ +using System; +using System.ComponentModel; +using System.IO; +using System.IO.Packaging; +using System.Linq; +using System.Net; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; +using System.Windows.Resources; +using Tango.AnimatedGif.Decoding; + +namespace Tango.AnimatedGif +{ + /// <summary> + /// Provides attached properties that display animated GIFs in a standard Image control. + /// </summary> + public static class ImageBehavior + { + #region Public attached properties and events + + /// <summary> + /// Gets the value of the <c>RepeatBehavior</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element from which to read the property value.</param> + /// <returns>The repeat behavior of the animated image.</returns> + [AttachedPropertyBrowsableForType(typeof(Image))] + public static double GetSpeedRatio(Image obj) + { + return (double)obj.GetValue(SpeedRatioProperty); + } + + /// <summary> + /// Sets the value of the <c>RepeatBehavior</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element on which to set the property value.</param> + /// <param name="value">The repeat behavior of the animated image.</param> + public static void SetSpeedRatio(Image obj, double value) + { + obj.SetValue(SpeedRatioProperty, value); + } + + /// <summary> + /// Identifies the <c>RepeatBehavior</c> attached property. + /// </summary> + public static readonly DependencyProperty SpeedRatioProperty = + DependencyProperty.RegisterAttached( + "SpeedRatio", + typeof(double), + typeof(ImageBehavior), + new UIPropertyMetadata( + 1.0, + SpeedRatioChanged)); + + private static void SpeedRatioChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var controller = GetAnimationController(d as Image); + + if (controller != null) + { + controller.SpeedRatio = (double)e.NewValue; + } + } + + /// <summary> + /// Gets the value of the <c>RepeatBehavior</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element from which to read the property value.</param> + /// <returns>The repeat behavior of the animated image.</returns> + [AttachedPropertyBrowsableForType(typeof(Image))] + public static bool GetEnableAnimation(Image obj) + { + return (bool)obj.GetValue(EnableAnimationProperty); + } + + /// <summary> + /// Sets the value of the <c>RepeatBehavior</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element on which to set the property value.</param> + /// <param name="value">The repeat behavior of the animated image.</param> + public static void SetEnableAnimation(Image obj, bool value) + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + obj.SetValue(EnableAnimationProperty, value); + })); + } + + /// <summary> + /// Identifies the <c>RepeatBehavior</c> attached property. + /// </summary> + public static readonly DependencyProperty EnableAnimationProperty = + DependencyProperty.RegisterAttached( + "EnableAnimation", + typeof(bool), + typeof(ImageBehavior), + new UIPropertyMetadata( + false, + EnableAnimationChanged)); + + private static void EnableAnimationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var controller = GetAnimationController(d as Image); + + if (controller != null) + { + if (e.NewValue != e.OldValue) + { + if ((bool)e.NewValue) + { + controller.SpeedRatio = GetSpeedRatio(d as Image); + controller.Play(); + } + else + { + controller.Pause(); + controller.GotoFrame(0); + } + } + } + } + + + /// <summary> + /// Gets the value of the <c>AnimatedSource</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element from which to read the property value.</param> + /// <returns>The currently displayed animated image.</returns> + [AttachedPropertyBrowsableForType(typeof(Image))] + public static ImageSource GetAnimatedSource(Image obj) + { + return (ImageSource)obj.GetValue(AnimatedSourceProperty); + } + + /// <summary> + /// Sets the value of the <c>AnimatedSource</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element on which to set the property value.</param> + /// <param name="value">The animated image to display.</param> + public static void SetAnimatedSource(Image obj, ImageSource value) + { + obj.SetValue(AnimatedSourceProperty, value); + } + + /// <summary> + /// Identifies the <c>AnimatedSource</c> attached property. + /// </summary> + public static readonly DependencyProperty AnimatedSourceProperty = + DependencyProperty.RegisterAttached( + "AnimatedSource", + typeof(ImageSource), + typeof(ImageBehavior), + new UIPropertyMetadata( + null, + AnimatedSourceChanged)); + + /// <summary> + /// Gets the value of the <c>RepeatBehavior</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element from which to read the property value.</param> + /// <returns>The repeat behavior of the animated image.</returns> + [AttachedPropertyBrowsableForType(typeof(Image))] + public static RepeatBehavior GetRepeatBehavior(Image obj) + { + return (RepeatBehavior)obj.GetValue(RepeatBehaviorProperty); + } + + /// <summary> + /// Sets the value of the <c>RepeatBehavior</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element on which to set the property value.</param> + /// <param name="value">The repeat behavior of the animated image.</param> + public static void SetRepeatBehavior(Image obj, RepeatBehavior value) + { + obj.SetValue(RepeatBehaviorProperty, value); + } + + /// <summary> + /// Identifies the <c>RepeatBehavior</c> attached property. + /// </summary> + public static readonly DependencyProperty RepeatBehaviorProperty = + DependencyProperty.RegisterAttached( + "RepeatBehavior", + typeof(RepeatBehavior), + typeof(ImageBehavior), + new UIPropertyMetadata( + default(RepeatBehavior), + RepeatBehaviorChanged)); + + /// <summary> + /// Gets the value of the <c>AnimateInDesignMode</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element from which to read the property value.</param> + /// <returns>true if GIF animations are shown in design mode; false otherwise.</returns> + public static bool GetAnimateInDesignMode(DependencyObject obj) + { + return (bool)obj.GetValue(AnimateInDesignModeProperty); + } + + /// <summary> + /// Sets the value of the <c>AnimateInDesignMode</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element on which to set the property value.</param> + /// <param name="value">true to show GIF animations in design mode; false otherwise.</param> + public static void SetAnimateInDesignMode(DependencyObject obj, bool value) + { + obj.SetValue(AnimateInDesignModeProperty, value); + } + + /// <summary> + /// Identifies the <c>AnimateInDesignMode</c> attached property. + /// </summary> + public static readonly DependencyProperty AnimateInDesignModeProperty = + DependencyProperty.RegisterAttached( + "AnimateInDesignMode", + typeof(bool), + typeof(ImageBehavior), + new FrameworkPropertyMetadata( + false, + FrameworkPropertyMetadataOptions.Inherits, + AnimateInDesignModeChanged)); + + /// <summary> + /// Gets the value of the <c>AutoStart</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element from which to read the property value.</param> + /// <returns>true if the animation should start immediately when loaded. Otherwise, false.</returns> + [AttachedPropertyBrowsableForType(typeof(Image))] + public static bool GetAutoStart(Image obj) + { + return (bool)obj.GetValue(AutoStartProperty); + } + + /// <summary> + /// Sets the value of the <c>AutoStart</c> attached property for the specified object. + /// </summary> + /// <param name="obj">The element from which to read the property value.</param> + /// <param name="value">true if the animation should start immediately when loaded. Otherwise, false.</param> + /// <remarks>The default value is true.</remarks> + public static void SetAutoStart(Image obj, bool value) + { + obj.SetValue(AutoStartProperty, value); + } + + /// <summary> + /// Identifies the <c>AutoStart</c> attached property. + /// </summary> + public static readonly DependencyProperty AutoStartProperty = + DependencyProperty.RegisterAttached("AutoStart", typeof(bool), typeof(ImageBehavior), new PropertyMetadata(false)); + + /// <summary> + /// Gets the animation controller for the specified <c>Image</c> control. + /// </summary> + /// <param name="imageControl"></param> + /// <returns></returns> + public static ImageAnimationController GetAnimationController(Image imageControl) + { + return (ImageAnimationController)imageControl.GetValue(AnimationControllerPropertyKey.DependencyProperty); + } + + private static void SetAnimationController(DependencyObject obj, ImageAnimationController value) + { + obj.SetValue(AnimationControllerPropertyKey, value); + } + + private static readonly DependencyPropertyKey AnimationControllerPropertyKey = + DependencyProperty.RegisterAttachedReadOnly("AnimationController", typeof(ImageAnimationController), typeof(ImageBehavior), new PropertyMetadata(null)); + + /// <summary> + /// Gets the value of the <c>IsAnimationLoaded</c> attached property for the specified object. + /// </summary> + /// <param name="image">The element from which to read the property value.</param> + /// <returns>true if the animation is loaded. Otherwise, false.</returns> + public static bool GetIsAnimationLoaded(Image image) + { + return (bool)image.GetValue(IsAnimationLoadedProperty); + } + + private static void SetIsAnimationLoaded(Image image, bool value) + { + image.SetValue(IsAnimationLoadedPropertyKey, value); + } + + private static readonly DependencyPropertyKey IsAnimationLoadedPropertyKey = + DependencyProperty.RegisterAttachedReadOnly("IsAnimationLoaded", typeof(bool), typeof(ImageBehavior), new PropertyMetadata(false)); + + /// <summary> + /// Identifies the <c>IsAnimationLoaded</c> attached property. + /// </summary> + public static readonly DependencyProperty IsAnimationLoadedProperty = + IsAnimationLoadedPropertyKey.DependencyProperty; + + /// <summary> + /// Identifies the <c>AnimationLoaded</c> attached event. + /// </summary> + public static readonly RoutedEvent AnimationLoadedEvent = + EventManager.RegisterRoutedEvent( + "AnimationLoaded", + RoutingStrategy.Bubble, + typeof(RoutedEventHandler), + typeof(ImageBehavior)); + + /// <summary> + /// Adds a handler for the AnimationLoaded attached event. + /// </summary> + /// <param name="image">The UIElement that listens to this event.</param> + /// <param name="handler">The event handler to be added.</param> + public static void AddAnimationLoadedHandler(Image image, RoutedEventHandler handler) + { + if (image == null) + throw new ArgumentNullException("image"); + if (handler == null) + throw new ArgumentNullException("handler"); + image.AddHandler(AnimationLoadedEvent, handler); + } + + /// <summary> + /// Removes a handler for the AnimationLoaded attached event. + /// </summary> + /// <param name="image">The UIElement that listens to this event.</param> + /// <param name="handler">The event handler to be removed.</param> + public static void RemoveAnimationLoadedHandler(Image image, RoutedEventHandler handler) + { + if (image == null) + throw new ArgumentNullException("image"); + if (handler == null) + throw new ArgumentNullException("handler"); + image.RemoveHandler(AnimationLoadedEvent, handler); + } + + /// <summary> + /// Identifies the <c>AnimationCompleted</c> attached event. + /// </summary> + public static readonly RoutedEvent AnimationCompletedEvent = + EventManager.RegisterRoutedEvent( + "AnimationCompleted", + RoutingStrategy.Bubble, + typeof(RoutedEventHandler), + typeof(ImageBehavior)); + + /// <summary> + /// Adds a handler for the AnimationCompleted attached event. + /// </summary> + /// <param name="d">The UIElement that listens to this event.</param> + /// <param name="handler">The event handler to be added.</param> + public static void AddAnimationCompletedHandler(Image d, RoutedEventHandler handler) + { + var element = d as UIElement; + if (element == null) + return; + element.AddHandler(AnimationCompletedEvent, handler); + } + + /// <summary> + /// Removes a handler for the AnimationCompleted attached event. + /// </summary> + /// <param name="d">The UIElement that listens to this event.</param> + /// <param name="handler">The event handler to be removed.</param> + public static void RemoveAnimationCompletedHandler(Image d, RoutedEventHandler handler) + { + var element = d as UIElement; + if (element == null) + return; + element.RemoveHandler(AnimationCompletedEvent, handler); + } + + #endregion + + private static void AnimatedSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + Image imageControl = o as Image; + if (imageControl == null) + return; + + var oldValue = e.OldValue as ImageSource; + var newValue = e.NewValue as ImageSource; + if (ReferenceEquals(oldValue, newValue)) + { + if (imageControl.IsLoaded) + { + var isAnimLoaded = GetIsAnimationLoaded(imageControl); + if (!isAnimLoaded) + InitAnimationOrImage(imageControl); + } + return; + } + if (oldValue != null) + { + imageControl.Loaded -= ImageControlLoaded; + imageControl.Unloaded -= ImageControlUnloaded; + AnimationCache.DecrementReferenceCount(oldValue, GetRepeatBehavior(imageControl)); + var controller = GetAnimationController(imageControl); + if (controller != null) + controller.Dispose(); + imageControl.Source = null; + } + if (newValue != null) + { + imageControl.Loaded += ImageControlLoaded; + imageControl.Unloaded += ImageControlUnloaded; + if (imageControl.IsLoaded) + InitAnimationOrImage(imageControl); + } + } + + private static void ImageControlLoaded(object sender, RoutedEventArgs e) + { + Image imageControl = sender as Image; + if (imageControl == null) + return; + InitAnimationOrImage(imageControl); + } + + static void ImageControlUnloaded(object sender, RoutedEventArgs e) + { + Image imageControl = sender as Image; + if (imageControl == null) + return; + var source = GetAnimatedSource(imageControl); + if (source != null) + AnimationCache.DecrementReferenceCount(source, GetRepeatBehavior(imageControl)); + var controller = GetAnimationController(imageControl); + if (controller != null) + controller.Dispose(); + } + + private static void RepeatBehaviorChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + Image imageControl = o as Image; + if (imageControl == null) + return; + + ImageSource source = GetAnimatedSource(imageControl); + if (source != null) + { + if (!Equals(e.OldValue, e.NewValue)) + AnimationCache.DecrementReferenceCount(source, (RepeatBehavior)e.OldValue); + if (imageControl.IsLoaded) + InitAnimationOrImage(imageControl); + } + } + + private static void AnimateInDesignModeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + Image imageControl = o as Image; + if (imageControl == null) + return; + + bool newValue = (bool)e.NewValue; + + ImageSource source = GetAnimatedSource(imageControl); + if (source != null && imageControl.IsLoaded) + { + if (newValue) + InitAnimationOrImage(imageControl); + else + imageControl.BeginAnimation(Image.SourceProperty, null); + } + } + + private static void InitAnimationOrImage(Image imageControl) + { + var controller = GetAnimationController(imageControl); + if (controller != null) + controller.Dispose(); + SetAnimationController(imageControl, null); + SetIsAnimationLoaded(imageControl, false); + + BitmapSource source = GetAnimatedSource(imageControl) as BitmapSource; + bool isInDesignMode = DesignerProperties.GetIsInDesignMode(imageControl); + bool animateInDesignMode = GetAnimateInDesignMode(imageControl); + bool shouldAnimate = !isInDesignMode || animateInDesignMode; + + // For a BitmapImage with a relative UriSource, the loading is deferred until + // BaseUri is set. This method will be called again when BaseUri is set. + bool isLoadingDeferred = IsLoadingDeferred(source); + + if (source != null && shouldAnimate && !isLoadingDeferred) + { + // Case of image being downloaded: retry after download is complete + if (source.IsDownloading) + { + EventHandler handler = null; + handler = (sender, args) => + { + source.DownloadCompleted -= handler; + InitAnimationOrImage(imageControl); + }; + source.DownloadCompleted += handler; + imageControl.Source = source; + return; + } + + var animation = GetAnimation(imageControl, source); + if (animation != null) + { + if (animation.KeyFrames.Count > 0) + { + // For some reason, it sometimes throws an exception the first time... the second time it works. + TryTwice(() => imageControl.Source = (ImageSource)animation.KeyFrames[0].Value); + } + else + { + imageControl.Source = source; + } + + controller = new ImageAnimationController(imageControl, animation, GetAutoStart(imageControl)); + SetAnimationController(imageControl, controller); + SetIsAnimationLoaded(imageControl, true); + imageControl.RaiseEvent(new RoutedEventArgs(AnimationLoadedEvent, imageControl)); + return; + } + } + imageControl.Source = source; + if (source != null) + { + SetIsAnimationLoaded(imageControl, true); + imageControl.RaiseEvent(new RoutedEventArgs(AnimationLoadedEvent, imageControl)); + } + } + + private static ObjectAnimationUsingKeyFrames GetAnimation(Image imageControl, BitmapSource source) + { + var animation = AnimationCache.GetAnimation(source, GetRepeatBehavior(imageControl)); + if (animation != null) + return animation; + GifFile gifMetadata; + var decoder = GetDecoder(source, out gifMetadata) as GifBitmapDecoder; + if (decoder != null && decoder.Frames.Count > 1) + { + var fullSize = GetFullSize(decoder, gifMetadata); + int index = 0; + animation = new ObjectAnimationUsingKeyFrames(); + var totalDuration = TimeSpan.Zero; + BitmapSource baseFrame = null; + foreach (var rawFrame in decoder.Frames) + { + var metadata = GetFrameMetadata(decoder, gifMetadata, index); + + var frame = MakeFrame(fullSize, rawFrame, metadata, baseFrame); + var keyFrame = new DiscreteObjectKeyFrame(frame, totalDuration); + animation.KeyFrames.Add(keyFrame); + + totalDuration += metadata.Delay; + + switch (metadata.DisposalMethod) + { + case FrameDisposalMethod.None: + case FrameDisposalMethod.DoNotDispose: + baseFrame = frame; + break; + case FrameDisposalMethod.RestoreBackground: + if (IsFullFrame(metadata, fullSize)) + { + baseFrame = null; + } + else + { + baseFrame = ClearArea(frame, metadata); + } + break; + case FrameDisposalMethod.RestorePrevious: + // Reuse same base frame + break; + } + + index++; + } + animation.Duration = totalDuration; + + animation.RepeatBehavior = GetActualRepeatBehavior(imageControl, decoder, gifMetadata); + + //Removed animation cache for PPC AsyncAdornerControl! + + //AnimationCache.AddAnimation(source, GetRepeatBehavior(imageControl), animation); + //AnimationCache.IncrementReferenceCount(source, GetRepeatBehavior(imageControl)); + return animation; + } + return null; + } + + private static BitmapSource ClearArea(BitmapSource frame, FrameMetadata metadata) + { + DrawingVisual visual = new DrawingVisual(); + using (var context = visual.RenderOpen()) + { + var fullRect = new Rect(0, 0, frame.PixelWidth, frame.PixelHeight); + var clearRect = new Rect(metadata.Left, metadata.Top, metadata.Width, metadata.Height); + var clip = Geometry.Combine( + new RectangleGeometry(fullRect), + new RectangleGeometry(clearRect), + GeometryCombineMode.Exclude, + null); + context.PushClip(clip); + context.DrawImage(frame, fullRect); + } + + var bitmap = new RenderTargetBitmap( + frame.PixelWidth, frame.PixelHeight, + frame.DpiX, frame.DpiY, + PixelFormats.Pbgra32); + bitmap.Render(visual); + + var result = new WriteableBitmap(bitmap); + + if (result.CanFreeze && !result.IsFrozen) + result.Freeze(); + return result; + } + + private static void TryTwice(Action action) + { + try + { + action(); + } + catch (Exception) + { + action(); + } + } + + private static bool IsLoadingDeferred(BitmapSource source) + { + var bmp = source as BitmapImage; + if (bmp == null) + return false; + if (bmp.UriSource != null && !bmp.UriSource.IsAbsoluteUri) + return bmp.BaseUri == null; + return false; + } + + private static BitmapDecoder GetDecoder(BitmapSource image, out GifFile gifFile) + { + gifFile = null; + BitmapDecoder decoder = null; + Stream stream = null; + Uri uri = null; + BitmapCreateOptions createOptions = BitmapCreateOptions.None; + + var bmp = image as BitmapImage; + if (bmp != null) + { + createOptions = bmp.CreateOptions; + if (bmp.StreamSource != null) + { + stream = bmp.StreamSource; + } + else if (bmp.UriSource != null) + { + uri = bmp.UriSource; + if (bmp.BaseUri != null && !uri.IsAbsoluteUri) + uri = new Uri(bmp.BaseUri, uri); + } + } + else + { + BitmapFrame frame = image as BitmapFrame; + if (frame != null) + { + decoder = frame.Decoder; + Uri.TryCreate(frame.BaseUri, frame.ToString(), out uri); + } + } + + if (decoder == null) + { + if (stream != null) + { + stream.Position = 0; + decoder = BitmapDecoder.Create(stream, createOptions, BitmapCacheOption.OnLoad); + } + else if (uri != null && uri.IsAbsoluteUri) + { + decoder = BitmapDecoder.Create(uri, createOptions, BitmapCacheOption.OnLoad); + } + } + + if (decoder is GifBitmapDecoder && !CanReadNativeMetadata(decoder)) + { + if (stream != null) + { + stream.Position = 0; + gifFile = GifFile.ReadGifFile(stream, true); + } + else if (uri != null) + { + gifFile = DecodeGifFile(uri); + } + else + { + throw new InvalidOperationException("Can't get URI or Stream from the source. AnimatedSource should be either a BitmapImage, or a BitmapFrame constructed from a URI."); + } + } + if (decoder == null) + { + throw new InvalidOperationException("Can't get a decoder from the source. AnimatedSource should be either a BitmapImage or a BitmapFrame."); + } + return decoder; + } + + private static bool CanReadNativeMetadata(BitmapDecoder decoder) + { + try + { + var m = decoder.Metadata; + return m != null; + } + catch + { + return false; + } + } + + private static GifFile DecodeGifFile(Uri uri) + { + Stream stream = null; + if (uri.Scheme == PackUriHelper.UriSchemePack) + { + StreamResourceInfo sri; + if (uri.Authority == "siteoforigin:,,,") + sri = Application.GetRemoteStream(uri); + else + sri = Application.GetResourceStream(uri); + + if (sri != null) + stream = sri.Stream; + } + else + { + WebClient wc = new WebClient(); + stream = wc.OpenRead(uri); + } + if (stream != null) + { + using (stream) + { + return GifFile.ReadGifFile(stream, true); + } + } + return null; + } + + private static bool IsFullFrame(FrameMetadata metadata, Int32Size fullSize) + { + return metadata.Left == 0 + && metadata.Top == 0 + && metadata.Width == fullSize.Width + && metadata.Height == fullSize.Height; + } + + private static BitmapSource MakeFrame( + Int32Size fullSize, + BitmapSource rawFrame, FrameMetadata metadata, + BitmapSource baseFrame) + { + if (baseFrame == null && IsFullFrame(metadata, fullSize)) + { + // No previous image to combine with, and same size as the full image + // Just return the frame as is + return rawFrame; + } + + DrawingVisual visual = new DrawingVisual(); + using (var context = visual.RenderOpen()) + { + if (baseFrame != null) + { + var fullRect = new Rect(0, 0, fullSize.Width, fullSize.Height); + context.DrawImage(baseFrame, fullRect); + } + + var rect = new Rect(metadata.Left, metadata.Top, metadata.Width, metadata.Height); + context.DrawImage(rawFrame, rect); + } + var bitmap = new RenderTargetBitmap( + fullSize.Width, fullSize.Height, + 96, 96, + PixelFormats.Pbgra32); + bitmap.Render(visual); + + var result = new WriteableBitmap(bitmap); + + if (result.CanFreeze && !result.IsFrozen) + result.Freeze(); + return result; + } + + private static RepeatBehavior GetActualRepeatBehavior(Image imageControl, BitmapDecoder decoder, GifFile gifMetadata) + { + // If specified explicitly, use this value + var repeatBehavior = GetRepeatBehavior(imageControl); + if (repeatBehavior != default(RepeatBehavior)) + return repeatBehavior; + + int repeatCount; + if (gifMetadata != null) + { + repeatCount = gifMetadata.RepeatCount; + } + else + { + repeatCount = GetRepeatCount(decoder); + } + if (repeatCount == 0) + return RepeatBehavior.Forever; + return new RepeatBehavior(repeatCount); + } + + private static int GetRepeatCount(BitmapDecoder decoder) + { + var ext = GetApplicationExtension(decoder, "NETSCAPE2.0"); + if (ext != null) + { + byte[] bytes = ext.GetQueryOrNull<byte[]>("/Data"); + if (bytes != null && bytes.Length >= 4) + return BitConverter.ToUInt16(bytes, 2); + } + return 1; + } + + private static BitmapMetadata GetApplicationExtension(BitmapDecoder decoder, string application) + { + int count = 0; + string query = "/appext"; + BitmapMetadata extension = decoder.Metadata.GetQueryOrNull<BitmapMetadata>(query); + while (extension != null) + { + byte[] bytes = extension.GetQueryOrNull<byte[]>("/Application"); + if (bytes != null) + { + string extApplication = Encoding.ASCII.GetString(bytes); + if (extApplication == application) + return extension; + } + query = string.Format("/[{0}]appext", ++count); + extension = decoder.Metadata.GetQueryOrNull<BitmapMetadata>(query); + } + return null; + } + + private static FrameMetadata GetFrameMetadata(BitmapDecoder decoder, GifFile gifMetadata, int frameIndex) + { + if (gifMetadata != null && gifMetadata.Frames.Count > frameIndex) + { + return GetFrameMetadata(gifMetadata.Frames[frameIndex]); + } + + return GetFrameMetadata(decoder.Frames[frameIndex]); + } + + private static FrameMetadata GetFrameMetadata(BitmapFrame frame) + { + var metadata = (BitmapMetadata)frame.Metadata; + var delay = TimeSpan.FromMilliseconds(100); + var metadataDelay = metadata.GetQueryOrDefault("/grctlext/Delay", 10); + if (metadataDelay != 0) + delay = TimeSpan.FromMilliseconds(metadataDelay * 10); + var disposalMethod = (FrameDisposalMethod)metadata.GetQueryOrDefault("/grctlext/Disposal", 0); + var frameMetadata = new FrameMetadata + { + Left = metadata.GetQueryOrDefault("/imgdesc/Left", 0), + Top = metadata.GetQueryOrDefault("/imgdesc/Top", 0), + Width = metadata.GetQueryOrDefault("/imgdesc/Width", frame.PixelWidth), + Height = metadata.GetQueryOrDefault("/imgdesc/Height", frame.PixelHeight), + Delay = delay, + DisposalMethod = disposalMethod + }; + return frameMetadata; + } + + private static FrameMetadata GetFrameMetadata(GifFrame gifMetadata) + { + var d = gifMetadata.Descriptor; + var frameMetadata = new FrameMetadata + { + Left = d.Left, + Top = d.Top, + Width = d.Width, + Height = d.Height, + Delay = TimeSpan.FromMilliseconds(100), + DisposalMethod = FrameDisposalMethod.None + }; + + var gce = gifMetadata.Extensions.OfType<GifGraphicControlExtension>().FirstOrDefault(); + if (gce != null) + { + if (gce.Delay != 0) + frameMetadata.Delay = TimeSpan.FromMilliseconds(gce.Delay); + frameMetadata.DisposalMethod = (FrameDisposalMethod)gce.DisposalMethod; + } + return frameMetadata; + } + + private static Int32Size GetFullSize(BitmapDecoder decoder, GifFile gifMetadata) + { + if (gifMetadata != null) + { + var lsd = gifMetadata.Header.LogicalScreenDescriptor; + return new Int32Size(lsd.Width, lsd.Height); + } + int width = decoder.Metadata.GetQueryOrDefault("/logscrdesc/Width", 0); + int height = decoder.Metadata.GetQueryOrDefault("/logscrdesc/Height", 0); + return new Int32Size(width, height); + } + + private struct Int32Size + { + public Int32Size(int width, int height) : this() + { + Width = width; + Height = height; + } + + public int Width { get; private set; } + public int Height { get; private set; } + } + + private class FrameMetadata + { + public int Left { get; set; } + public int Top { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public TimeSpan Delay { get; set; } + public FrameDisposalMethod DisposalMethod { get; set; } + } + + private enum FrameDisposalMethod + { + None = 0, + DoNotDispose = 1, + RestoreBackground = 2, + RestorePrevious = 3 + } + + private static T GetQueryOrDefault<T>(this BitmapMetadata metadata, string query, T defaultValue) + { + if (metadata.ContainsQuery(query)) + return (T)Convert.ChangeType(metadata.GetQuery(query), typeof(T)); + return defaultValue; + } + + private static T GetQueryOrNull<T>(this BitmapMetadata metadata, string query) + where T : class + { + if (metadata.ContainsQuery(query)) + return metadata.GetQuery(query) as T; + return null; + } + + // For debug purposes + //private static void Save(BitmapSource image, string path) + //{ + // var encoder = new PngBitmapEncoder(); + // encoder.Frames.Add(BitmapFrame.Create(image)); + // using (var stream = File.OpenWrite(path)) + // { + // encoder.Save(stream); + // } + //} + } +} diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Properties/AssemblyInfo.cs b/Software/Visual_Studio/Tango.AnimatedGif/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..2c8b459c4 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Properties/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Markup; + +[assembly: AssemblyTitle("Tango - GIF Animation Library")] +[assembly: ComVisible(false)] + +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
\ No newline at end of file diff --git a/Software/Visual_Studio/Tango.AnimatedGif/Tango.AnimatedGif.csproj b/Software/Visual_Studio/Tango.AnimatedGif/Tango.AnimatedGif.csproj new file mode 100644 index 000000000..18d991271 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/Tango.AnimatedGif.csproj @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{D129789C-3096-4D0B-8DD7-FE24A4DF4B21}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Tango.AnimatedGif</RootNamespace> + <AssemblyName>Tango.AnimatedGif</AssemblyName> + <TargetFrameworkVersion>v4.6</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <TargetFrameworkProfile> + </TargetFrameworkProfile> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>..\Build\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <Prefer32Bit>false</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>..\Build\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <DocumentationFile> + </DocumentationFile> + <Prefer32Bit>false</Prefer32Bit> + </PropertyGroup> + <ItemGroup> + <Reference Include="PresentationCore" /> + <Reference Include="PresentationFramework" /> + <Reference Include="System" /> + <Reference Include="System.Xaml" /> + <Reference Include="System.Xml" /> + <Reference Include="WindowsBase" /> + </ItemGroup> + <ItemGroup> + <Compile Include="..\Versioning\Core.cs"> + <Link>Core.cs</Link> + </Compile> + <Compile Include="AnimationCache.cs" /> + <Compile Include="Decoding\GifApplicationExtension.cs" /> + <Compile Include="Decoding\GifBlock.cs" /> + <Compile Include="Decoding\GifBlockKind.cs" /> + <Compile Include="Decoding\GifColor.cs" /> + <Compile Include="Decoding\GifCommentExtension.cs" /> + <Compile Include="Decoding\GifDecoderException.cs" /> + <Compile Include="Decoding\GifExtension.cs" /> + <Compile Include="Decoding\GifFile.cs" /> + <Compile Include="Decoding\GifFrame.cs" /> + <Compile Include="Decoding\GifGraphicControlExtension.cs" /> + <Compile Include="Decoding\GifHeader.cs" /> + <Compile Include="Decoding\GifHelpers.cs" /> + <Compile Include="Decoding\GifImageData.cs" /> + <Compile Include="Decoding\GifImageDescriptor.cs" /> + <Compile Include="Decoding\GifLogicalScreenDescriptor.cs" /> + <Compile Include="Decoding\GifPlainTextExtension.cs" /> + <Compile Include="Decoding\GifTrailer.cs" /> + <Compile Include="ImageAnimationController.cs" /> + <Compile Include="ImageBehavior.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="UserControl1.xaml.cs"> + <DependentUpon>UserControl1.xaml</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <Page Include="UserControl1.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/Software/Visual_Studio/Tango.AnimatedGif/UserControl1.xaml b/Software/Visual_Studio/Tango.AnimatedGif/UserControl1.xaml new file mode 100644 index 000000000..1ae2a1cd4 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/UserControl1.xaml @@ -0,0 +1,12 @@ +<UserControl x:Class="Tango.AnimatedGif.UserControl1" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:Tango.AnimatedGif" + mc:Ignorable="d" + d:DesignHeight="300" d:DesignWidth="300"> + <Grid> + + </Grid> +</UserControl> diff --git a/Software/Visual_Studio/Tango.AnimatedGif/UserControl1.xaml.cs b/Software/Visual_Studio/Tango.AnimatedGif/UserControl1.xaml.cs new file mode 100644 index 000000000..8d90270d3 --- /dev/null +++ b/Software/Visual_Studio/Tango.AnimatedGif/UserControl1.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.AnimatedGif +{ + /// <summary> + /// Interaction logic for UserControl1.xaml + /// </summary> + public partial class UserControl1 : UserControl + { + public UserControl1() + { + InitializeComponent(); + } + } +} diff --git a/Software/Visual_Studio/Tango.BL/ColorConversion/TangoColorConverter.cs b/Software/Visual_Studio/Tango.BL/ColorConversion/TangoColorConverter.cs index 210d4d3e8..147309f54 100644 --- a/Software/Visual_Studio/Tango.BL/ColorConversion/TangoColorConverter.cs +++ b/Software/Visual_Studio/Tango.BL/ColorConversion/TangoColorConverter.cs @@ -82,6 +82,11 @@ namespace Tango.BL.ColorConversion return GetSuggestions(brushStop).OutOfGamut; } + public static int GetLeastCommonProcessParametersTableIndex(IEnumerable<BrushStop> stops) + { + return stops.Select(x => GetSuggestions(x)).Select(x => x.SingleCoordinates).Min(x => x.ProcessParametersTableIndex); + } + private static ConversionInput CreateConversionInput(BrushStop brushStop) { ConversionInput conversionInput = new ConversionInput(); diff --git a/Software/Visual_Studio/Tango.BL/Entities/HardwareMotor.cs b/Software/Visual_Studio/Tango.BL/Entities/HardwareMotor.cs index 3104dfafd..cb7abbd64 100644 --- a/Software/Visual_Studio/Tango.BL/Entities/HardwareMotor.cs +++ b/Software/Visual_Studio/Tango.BL/Entities/HardwareMotor.cs @@ -23,7 +23,7 @@ namespace Tango.BL.Entities /// <summary> /// - ///</summary> + /// </summary> [Table("HARDWARE_MOTORS")] public partial class HardwareMotor : ObservableEntity<HardwareMotor> @@ -584,8 +584,8 @@ namespace Tango.BL.Entities protected Int32 _fsspd; /// <summary> - ///The speed in which the motor moves to full step operation. - ///</summary> + /// The speed in which the motor moves to full step operation. + /// </summary> [Column("FS_SPD")] diff --git a/Software/Visual_Studio/Tango.BL/Entities/Job.cs b/Software/Visual_Studio/Tango.BL/Entities/Job.cs index 15ac3d199..0adfe0b23 100644 --- a/Software/Visual_Studio/Tango.BL/Entities/Job.cs +++ b/Software/Visual_Studio/Tango.BL/Entities/Job.cs @@ -23,7 +23,7 @@ namespace Tango.BL.Entities /// <summary> /// - ///</summary> + /// </summary> [Table("JOBS")] public partial class Job : ObservableEntity<Job> @@ -433,13 +433,13 @@ namespace Tango.BL.Entities protected Int32 _status; /// <summary> - ///0 = Draft - ///1 = PendingApproval - ///2 = Ready - ///3 = Approved - ///4 = Disrupted - ///5 = Completed - ///</summary> + /// 0 = Draft + /// 1 = PendingApproval + /// 2 = Ready + /// 3 = Approved + /// 4 = Disrupted + /// 5 = Completed + /// </summary> [Column("STATUS")] @@ -505,9 +505,9 @@ namespace Tango.BL.Entities protected Int32 _type; /// <summary> - ///0 = Sewing - ///1 = Embroidery - ///</summary> + /// 0 = Sewing + /// 1 = Embroidery + /// </summary> [Column("TYPE")] @@ -551,9 +551,9 @@ namespace Tango.BL.Entities protected Int32 _spoolsdistribution; /// <summary> - ///0 = All segments per spool - ///1 = One segments per spool - ///</summary> + /// 0 = All segments per spool + /// 1 = One segments per spool + /// </summary> [Column("SPOOLS_DISTRIBUTION")] @@ -593,6 +593,28 @@ namespace Tango.BL.Entities } + protected Int32 _sampleunitsormeters; + + /// <summary> + /// Gets or sets the job sample units or meters. + /// </summary> + + [Column("SAMPLE_UNITS_OR_METERS")] + + public Int32 SampleUnitsOrMeters + { + get + { + return _sampleunitsormeters; + } + + set + { + _sampleunitsormeters = value; RaisePropertyChanged(nameof(SampleUnitsOrMeters)); + } + + } + protected ColorSpace _colorspace; /// <summary> diff --git a/Software/Visual_Studio/Tango.BL/EntitiesExtensions/Job.cs b/Software/Visual_Studio/Tango.BL/EntitiesExtensions/Job.cs index 4c68f3c72..292fe000d 100644 --- a/Software/Visual_Studio/Tango.BL/EntitiesExtensions/Job.cs +++ b/Software/Visual_Studio/Tango.BL/EntitiesExtensions/Job.cs @@ -56,6 +56,19 @@ namespace Tango.BL.Entities } /// <summary> + /// Gets the total job segments length multiplied by number of units if it is an embroidery job. + /// </summary> + [NotMapped] + [JsonIgnore] + public double LengthIncludingNumberOfUnits + { + get + { + return _lastLength * NumberOfUnits; + } + } + + /// <summary> /// Gets or sets the job <see cref="Status"/> property as <see cref="JobStatus"/> enum instead of int. /// </summary> [NotMapped] @@ -193,6 +206,7 @@ namespace Tango.BL.Entities if (_lastLength != GetLength()) { RaisePropertyChanged(nameof(Length)); + RaisePropertyChanged(nameof(LengthIncludingNumberOfUnits)); LengthChanged?.Invoke(this, new EventArgs()); } } @@ -349,7 +363,7 @@ namespace Tango.BL.Entities /// <returns></returns> public TimeSpan GetEstimatedDuration(ProcessParametersTable processParameters) { - return TimeSpan.FromSeconds(Length / (processParameters.DyeingSpeed / 100d)); + return TimeSpan.FromSeconds(LengthIncludingNumberOfUnits / (processParameters.DyeingSpeed / 100d)); } /// <summary> diff --git a/Software/Visual_Studio/Tango.Core/ExtensionMethods/IEnumerableExtensions.cs b/Software/Visual_Studio/Tango.Core/ExtensionMethods/IEnumerableExtensions.cs index ffe164a53..0f15ad060 100644 --- a/Software/Visual_Studio/Tango.Core/ExtensionMethods/IEnumerableExtensions.cs +++ b/Software/Visual_Studio/Tango.Core/ExtensionMethods/IEnumerableExtensions.cs @@ -80,5 +80,17 @@ public static class IEnumerableExtensions { return String.Join(separator, source); } + + /// <summary> + /// Returns a distinct collection by the specified property. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="source">The source.</param> + /// <param name="property">The property.</param> + /// <returns></returns> + public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> property) + { + return source.GroupBy(property).Select(g => g.First()); + } } diff --git a/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB.cs b/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB.cs index 433dacee4..109e876b5 100644 --- a/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB.cs +++ b/Software/Visual_Studio/Tango.DAL.Remote/DB/JOB.cs @@ -49,6 +49,7 @@ namespace Tango.DAL.Remote.DB public string CUSTOMER_GUID { get; set; } public int SPOOLS_DISTRIBUTION { get; set; } public int NUMBER_OF_HEADS { get; set; } + public int SAMPLE_UNITS_OR_METERS { get; set; } public virtual COLOR_SPACES COLOR_SPACES { get; set; } public virtual CUSTOMER CUSTOMER { get; set; } diff --git a/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx b/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx index 7f17f8940..9030a9f0b 100644 --- a/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx +++ b/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx @@ -582,6 +582,7 @@ <Property Name="CUSTOMER_GUID" Type="varchar" MaxLength="36" /> <Property Name="SPOOLS_DISTRIBUTION" Type="int" Nullable="false" /> <Property Name="NUMBER_OF_HEADS" Type="int" Nullable="false" /> + <Property Name="SAMPLE_UNITS_OR_METERS" Type="int" Nullable="false" /> </EntityType> <EntityType Name="LINEAR_MASS_DENSITY_UNITS"> <Key> @@ -3397,6 +3398,7 @@ <Property Name="CUSTOMER_GUID" Type="String" MaxLength="36" FixedLength="false" Unicode="false" /> <Property Name="SPOOLS_DISTRIBUTION" Type="Int32" Nullable="false" /> <Property Name="NUMBER_OF_HEADS" Type="Int32" Nullable="false" /> + <Property Name="SAMPLE_UNITS_OR_METERS" Type="Int32" Nullable="false" /> <NavigationProperty Name="COLOR_SPACES" Relationship="RemoteModel.FK_JOBS_COLOR_SPACES" FromRole="JOB" ToRole="COLOR_SPACES" /> <NavigationProperty Name="CUSTOMER" Relationship="RemoteModel.FK_JOBS_CUSTOMERS" FromRole="JOB" ToRole="CUSTOMER" /> <NavigationProperty Name="JOB_RUNS" Relationship="RemoteModel.FK_JOB_RUNS_JOBS" FromRole="JOB" ToRole="JOB_RUNS" /> @@ -5462,6 +5464,7 @@ <EntitySetMapping Name="JOBS"> <EntityTypeMapping TypeName="RemoteModel.JOB"> <MappingFragment StoreEntitySet="JOBS"> + <ScalarProperty Name="SAMPLE_UNITS_OR_METERS" ColumnName="SAMPLE_UNITS_OR_METERS" /> <ScalarProperty Name="NUMBER_OF_HEADS" ColumnName="NUMBER_OF_HEADS" /> <ScalarProperty Name="SPOOLS_DISTRIBUTION" ColumnName="SPOOLS_DISTRIBUTION" /> <ScalarProperty Name="CUSTOMER_GUID" ColumnName="CUSTOMER_GUID" /> diff --git a/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx.diagram b/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx.diagram index 668abf151..305fe3ebd 100644 --- a/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx.diagram +++ b/Software/Visual_Studio/Tango.DAL.Remote/DB/RemoteADO.edmx.diagram @@ -5,78 +5,78 @@ <!-- Diagram content (shape and connector positions) --> <edmx:Diagrams> <Diagram DiagramId="f9ae01d708754bbd997add25a4bacc79" Name="Diagram1"> - <EntityTypeShape EntityType="RemoteModel.ACTION_TYPES" Width="1.5" PointX="6" PointY="75.5" /> - <EntityTypeShape EntityType="RemoteModel.ADDRESS" Width="1.5" PointX="1.5" PointY="37.625" /> - <EntityTypeShape EntityType="RemoteModel.APPLICATION_DISPLAY_PANEL_VERSIONS" Width="1.5" PointX="1.5" PointY="76.625" /> - <EntityTypeShape EntityType="RemoteModel.APPLICATION_FIRMWARE_VERSIONS" Width="1.5" PointX="1.5" PointY="73.625" /> - <EntityTypeShape EntityType="RemoteModel.APPLICATION_OS_VERSIONS" Width="1.5" PointX="1.5" PointY="79.5" /> - <EntityTypeShape EntityType="RemoteModel.APPLICATION_VERSIONS" Width="1.5" PointX="1.5" PointY="70.75" /> - <EntityTypeShape EntityType="RemoteModel.BRUSH_STOPS" Width="1.5" PointX="12.75" PointY="11.125" /> - <EntityTypeShape EntityType="RemoteModel.CARTRIDGE_TYPES" Width="1.5" PointX="3" PointY="4.5" /> - <EntityTypeShape EntityType="RemoteModel.CAT" Width="1.5" PointX="5.25" PointY="10.75" /> - <EntityTypeShape EntityType="RemoteModel.CCT" Width="1.5" PointX="5.25" PointY="34" /> - <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS" Width="1.5" PointX="10.5" PointY="5.5" /> - <EntityTypeShape EntityType="RemoteModel.COLOR_SPACES" Width="1.5" PointX="6" PointY="18.5" /> - <EntityTypeShape EntityType="RemoteModel.CONFIGURATION" Width="1.5" PointX="0.75" PointY="61.625" /> - <EntityTypeShape EntityType="RemoteModel.CONTACT" Width="1.5" PointX="1.5" PointY="50" /> - <EntityTypeShape EntityType="RemoteModel.CUSTOMER" Width="1.5" PointX="6" PointY="22" /> - <EntityTypeShape EntityType="RemoteModel.DISPENSER_TYPES" Width="1.5" PointX="3" PointY="95.375" /> - <EntityTypeShape EntityType="RemoteModel.EMBEDDED_FIRMWARE_VERSIONS" Width="1.5" PointX="1.5" PointY="67.875" /> - <EntityTypeShape EntityType="RemoteModel.EMBEDDED_SOFTWARE_VERSIONS" Width="1.5" PointX="1.5" PointY="57.875" /> - <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES" Width="1.5" PointX="6" PointY="70.625" /> - <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES_ACTIONS" Width="1.5" PointX="8.25" PointY="71.5" /> - <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES_CATEGORIES" Width="1.5" PointX="3.75" PointY="74.75" /> - <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES_GROUPS" Width="1.5" PointX="3.75" PointY="71.625" /> - <EntityTypeShape EntityType="RemoteModel.FIBER_SHAPES" Width="1.5" PointX="0.75" PointY="27.5" /> - <EntityTypeShape EntityType="RemoteModel.FIBER_SYNTHS" Width="1.5" PointX="0.75" PointY="21.625" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_DANCER_TYPES" Width="1.5" PointX="4.5" PointY="64.375" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_DANCERS" Width="1.5" PointX="6.75" PointY="53.5" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_MOTOR_TYPES" Width="1.5" PointX="7.5" PointY="49.375" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_MOTORS" Width="1.5" PointX="9.75" PointY="52.125" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_PID_CONTROL_TYPES" Width="1.5" PointX="1.5" PointY="91.375" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_PID_CONTROLS" Width="1.5" PointX="3.75" PointY="57.75" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_SPEED_SENSOR_TYPES" Width="1.5" PointX="1.5" PointY="87.375" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_SPEED_SENSORS" Width="1.5" PointX="3.75" PointY="54.25" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_VERSIONS" Width="1.5" PointX="1.5" PointY="54" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_WINDER_TYPES" Width="1.5" PointX="1.5" PointY="83.375" /> - <EntityTypeShape EntityType="RemoteModel.HARDWARE_WINDERS" Width="1.5" PointX="3.75" PointY="50.375" /> - <EntityTypeShape EntityType="RemoteModel.HTML_PAGES" Width="1.5" PointX="3.75" PointY="68.25" /> - <EntityTypeShape EntityType="RemoteModel.IDS_PACK_FORMULAS" Width="1.5" PointX="3" PointY="8.25" /> - <EntityTypeShape EntityType="RemoteModel.IDS_PACKS" Width="1.5" PointX="5.25" PointY="43.75" /> - <EntityTypeShape EntityType="RemoteModel.JOB_RUNS" Width="1.5" PointX="10.5" PointY="16.75" /> - <EntityTypeShape EntityType="RemoteModel.JOB" Width="1.5" PointX="8.25" PointY="12.25" /> - <EntityTypeShape EntityType="RemoteModel.LINEAR_MASS_DENSITY_UNITS" Width="1.5" PointX="0.75" PointY="10.875" /> - <EntityTypeShape EntityType="RemoteModel.LIQUID_TYPES" Width="1.5" PointX="3" PointY="21" /> - <EntityTypeShape EntityType="RemoteModel.LIQUID_TYPES_RMLS" Width="1.5" PointX="5.25" PointY="15" /> - <EntityTypeShape EntityType="RemoteModel.MACHINE_STUDIO_VERSIONS" Width="1.5" PointX="8.25" PointY="38" /> - <EntityTypeShape EntityType="RemoteModel.MACHINE_VERSIONS" Width="1.5" PointX="0.75" PointY="46.5" /> - <EntityTypeShape EntityType="RemoteModel.MACHINE" Width="1.5" PointX="3" PointY="44.625" /> - <EntityTypeShape EntityType="RemoteModel.MACHINES_CONFIGURATIONS" Width="1.5" PointX="6.25" PointY="58.625" /> - <EntityTypeShape EntityType="RemoteModel.MACHINES_EVENTS" Width="1.5" PointX="8.25" PointY="42" /> + <EntityTypeShape EntityType="RemoteModel.ACTION_TYPES" Width="1.5" PointX="6" PointY="98.25" /> + <EntityTypeShape EntityType="RemoteModel.ADDRESS" Width="1.5" PointX="1.5" PointY="43.5" /> + <EntityTypeShape EntityType="RemoteModel.APPLICATION_DISPLAY_PANEL_VERSIONS" Width="1.5" PointX="1.5" PointY="70.25" /> + <EntityTypeShape EntityType="RemoteModel.APPLICATION_FIRMWARE_VERSIONS" Width="1.5" PointX="1.5" PointY="57.5" /> + <EntityTypeShape EntityType="RemoteModel.APPLICATION_OS_VERSIONS" Width="1.5" PointX="1.5" PointY="60.5" /> + <EntityTypeShape EntityType="RemoteModel.APPLICATION_VERSIONS" Width="1.5" PointX="1.5" PointY="76" /> + <EntityTypeShape EntityType="RemoteModel.BRUSH_STOPS" Width="1.5" PointX="12.75" PointY="14.5" /> + <EntityTypeShape EntityType="RemoteModel.CARTRIDGE_TYPES" Width="1.5" PointX="6.75" PointY="84.75" /> + <EntityTypeShape EntityType="RemoteModel.CAT" Width="1.5" PointX="5.25" PointY="19.75" /> + <EntityTypeShape EntityType="RemoteModel.CCT" Width="1.5" PointX="5.25" PointY="11.375" /> + <EntityTypeShape EntityType="RemoteModel.COLOR_CATALOGS" Width="1.5" PointX="10.5" PointY="20.875" /> + <EntityTypeShape EntityType="RemoteModel.COLOR_SPACES" Width="1.5" PointX="6" PointY="23.75" /> + <EntityTypeShape EntityType="RemoteModel.CONFIGURATION" Width="1.5" PointX="3.75" PointY="65.125" /> + <EntityTypeShape EntityType="RemoteModel.CONTACT" Width="1.5" PointX="1.5" PointY="47.75" /> + <EntityTypeShape EntityType="RemoteModel.CUSTOMER" Width="1.5" PointX="6" PointY="46.125" /> + <EntityTypeShape EntityType="RemoteModel.DISPENSER_TYPES" Width="1.5" PointX="6.75" PointY="81.375" /> + <EntityTypeShape EntityType="RemoteModel.EMBEDDED_FIRMWARE_VERSIONS" Width="1.5" PointX="1.5" PointY="67.25" /> + <EntityTypeShape EntityType="RemoteModel.EMBEDDED_SOFTWARE_VERSIONS" Width="1.5" PointX="1.5" PointY="73.125" /> + <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES" Width="1.5" PointX="6" PointY="93.375" /> + <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES_ACTIONS" Width="1.5" PointX="8.25" PointY="94.25" /> + <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES_CATEGORIES" Width="1.5" PointX="3.75" PointY="97.5" /> + <EntityTypeShape EntityType="RemoteModel.EVENT_TYPES_GROUPS" Width="1.5" PointX="3.75" PointY="94.375" /> + <EntityTypeShape EntityType="RemoteModel.FIBER_SHAPES" Width="1.5" PointX="0.75" PointY="18.75" /> + <EntityTypeShape EntityType="RemoteModel.FIBER_SYNTHS" Width="1.5" PointX="0.75" PointY="27.5" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_DANCER_TYPES" Width="1.5" PointX="4.5" PointY="71.75" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_DANCERS" Width="1.5" PointX="6.75" PointY="55.875" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_MOTOR_TYPES" Width="1.5" PointX="6.5" PointY="77.75" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_MOTORS" Width="1.5" PointX="8.75" PointY="61.5" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_PID_CONTROL_TYPES" Width="1.5" PointX="1.5" PointY="53.75" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_PID_CONTROLS" Width="1.5" PointX="3.75" PointY="58.125" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_SPEED_SENSOR_TYPES" Width="1.5" PointX="4.5" PointY="81.75" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_SPEED_SENSORS" Width="1.5" PointX="6.75" PointY="69.625" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_VERSIONS" Width="1.5" PointX="1.5" PointY="63.375" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_WINDER_TYPES" Width="1.5" PointX="7.5" PointY="73.75" /> + <EntityTypeShape EntityType="RemoteModel.HARDWARE_WINDERS" Width="1.5" PointX="9.75" PointY="57.75" /> + <EntityTypeShape EntityType="RemoteModel.HTML_PAGES" Width="1.5" PointX="3.75" PointY="91" /> + <EntityTypeShape EntityType="RemoteModel.IDS_PACK_FORMULAS" Width="1.5" PointX="6.75" PointY="87.625" /> + <EntityTypeShape EntityType="RemoteModel.IDS_PACKS" Width="1.5" PointX="9" PointY="44" /> + <EntityTypeShape EntityType="RemoteModel.JOB_RUNS" Width="1.5" PointX="10.5" PointY="13.25" /> + <EntityTypeShape EntityType="RemoteModel.JOB" Width="1.5" PointX="8.25" PointY="12.125" /> + <EntityTypeShape EntityType="RemoteModel.LINEAR_MASS_DENSITY_UNITS" Width="1.5" PointX="0.75" PointY="21.625" /> + <EntityTypeShape EntityType="RemoteModel.LIQUID_TYPES" Width="1.5" PointX="3" PointY="21.875" /> + <EntityTypeShape EntityType="RemoteModel.LIQUID_TYPES_RMLS" Width="1.5" PointX="5.25" PointY="16" /> + <EntityTypeShape EntityType="RemoteModel.MACHINE_STUDIO_VERSIONS" Width="1.5" PointX="8.25" PointY="39.125" /> + <EntityTypeShape EntityType="RemoteModel.MACHINE_VERSIONS" Width="1.5" PointX="3.75" PointY="54.25" /> + <EntityTypeShape EntityType="RemoteModel.MACHINE" Width="1.5" PointX="6" PointY="64" /> + <EntityTypeShape EntityType="RemoteModel.MACHINES_CONFIGURATIONS" Width="1.5" PointX="11.25" PointY="66.125" /> + <EntityTypeShape EntityType="RemoteModel.MACHINES_EVENTS" Width="1.5" PointX="8.25" PointY="51.75" /> <EntityTypeShape EntityType="RemoteModel.MEDIA_COLORS" Width="1.5" PointX="0.75" PointY="15" /> - <EntityTypeShape EntityType="RemoteModel.MEDIA_CONDITIONS" Width="1.5" PointX="0.75" PointY="18.75" /> - <EntityTypeShape EntityType="RemoteModel.MEDIA_MATERIALS" Width="1.5" PointX="0.75" PointY="7.875" /> + <EntityTypeShape EntityType="RemoteModel.MEDIA_CONDITIONS" Width="1.5" PointX="0.75" PointY="7.875" /> + <EntityTypeShape EntityType="RemoteModel.MEDIA_MATERIALS" Width="1.5" PointX="0.75" PointY="10.875" /> <EntityTypeShape EntityType="RemoteModel.MEDIA_PURPOSES" Width="1.5" PointX="0.75" PointY="24.625" /> - <EntityTypeShape EntityType="RemoteModel.MID_TANK_TYPES" Width="1.5" PointX="3" PointY="25.125" /> - <EntityTypeShape EntityType="RemoteModel.ORGANIZATION" Width="1.5" PointX="0.75" PointY="32.875" /> - <EntityTypeShape EntityType="RemoteModel.PERMISSION" Width="1.5" PointX="9" PointY="24.25" /> - <EntityTypeShape EntityType="RemoteModel.PROCESS_PARAMETERS_TABLES" Width="1.5" PointX="7.5" PointY="27.375" /> - <EntityTypeShape EntityType="RemoteModel.PROCESS_PARAMETERS_TABLES_GROUPS" Width="1.5" PointX="5.25" PointY="29" /> + <EntityTypeShape EntityType="RemoteModel.MID_TANK_TYPES" Width="1.5" PointX="6.75" PointY="7.25" /> + <EntityTypeShape EntityType="RemoteModel.ORGANIZATION" Width="1.5" PointX="3.75" PointY="45.625" /> + <EntityTypeShape EntityType="RemoteModel.PERMISSION" Width="1.5" PointX="9" PointY="8.375" /> + <EntityTypeShape EntityType="RemoteModel.PROCESS_PARAMETERS_TABLES" Width="1.5" PointX="7.5" PointY="31.75" /> + <EntityTypeShape EntityType="RemoteModel.PROCESS_PARAMETERS_TABLES_GROUPS" Width="1.5" PointX="5.25" PointY="33.25" /> <EntityTypeShape EntityType="RemoteModel.RML" Width="1.5" PointX="3" PointY="12.375" /> - <EntityTypeShape EntityType="RemoteModel.ROLE" Width="1.5" PointX="9" PointY="34.125" /> - <EntityTypeShape EntityType="RemoteModel.ROLES_PERMISSIONS" Width="1.5" PointX="11.25" PointY="34.25" /> - <EntityTypeShape EntityType="RemoteModel.SEGMENT" Width="1.5" PointX="10.5" PointY="13.25" /> - <EntityTypeShape EntityType="RemoteModel.SPOOL_TYPES" Width="1.5" PointX="6" PointY="6" /> + <EntityTypeShape EntityType="RemoteModel.ROLE" Width="1.5" PointX="9" PointY="28.25" /> + <EntityTypeShape EntityType="RemoteModel.ROLES_PERMISSIONS" Width="1.5" PointX="11.25" PointY="28.375" /> + <EntityTypeShape EntityType="RemoteModel.SEGMENT" Width="1.5" PointX="10.5" PointY="16.625" /> + <EntityTypeShape EntityType="RemoteModel.SPOOL_TYPES" Width="1.5" PointX="6" PointY="49.75" /> <EntityTypeShape EntityType="RemoteModel.SYNC_CONFIGURATIONS" Width="1.5" PointX="0.75" PointY="0.75" /> <EntityTypeShape EntityType="RemoteModel.sysdiagram" Width="1.5" PointX="2.75" PointY="0.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_CONTROLLERS" Width="1.5" PointX="8.75" PointY="0.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_DISPENSERS" Width="1.5" PointX="10.75" PointY="0.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_IOS" Width="1.5" PointX="12.75" PointY="0.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_MONITORS" Width="1.5" PointX="12.75" PointY="5.75" /> - <EntityTypeShape EntityType="RemoteModel.TECH_VALVES" Width="1.5" PointX="14.75" PointY="0.75" /> - <EntityTypeShape EntityType="RemoteModel.USER" Width="1.5" PointX="6" PointY="38.25" /> - <EntityTypeShape EntityType="RemoteModel.USERS_ROLES" Width="1.5" PointX="11.25" PointY="39.125" /> - <EntityTypeShape EntityType="RemoteModel.WINDING_METHODS" Width="1.5" PointX="6" PointY="2.125" /> + <EntityTypeShape EntityType="RemoteModel.TECH_CONTROLLERS" Width="1.5" PointX="0.75" PointY="3.75" /> + <EntityTypeShape EntityType="RemoteModel.TECH_DISPENSERS" Width="1.5" PointX="2.75" PointY="3.75" /> + <EntityTypeShape EntityType="RemoteModel.TECH_IOS" Width="1.5" PointX="4.75" PointY="0.75" /> + <EntityTypeShape EntityType="RemoteModel.TECH_MONITORS" Width="1.5" PointX="6.75" PointY="0.75" /> + <EntityTypeShape EntityType="RemoteModel.TECH_VALVES" Width="1.5" PointX="2.75" PointY="6.75" /> + <EntityTypeShape EntityType="RemoteModel.USER" Width="1.5" PointX="6" PointY="38.375" /> + <EntityTypeShape EntityType="RemoteModel.USERS_ROLES" Width="1.5" PointX="11.25" PointY="39.25" /> + <EntityTypeShape EntityType="RemoteModel.WINDING_METHODS" Width="1.5" PointX="6" PointY="27.25" /> <AssociationConnector Association="RemoteModel.FK_EVENTS_ACTIONS_ACTIONS" /> <AssociationConnector Association="RemoteModel.FK_ORGANIZATIONS_ADDRESSES" /> <AssociationConnector Association="RemoteModel.FK_USERS_ADDRESSES" /> diff --git a/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs b/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs index c0bf390c0..24ec40fc3 100644 --- a/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs +++ b/Software/Visual_Studio/Tango.Integration/Operation/IMachineOperator.cs @@ -94,6 +94,15 @@ namespace Tango.Integration.Operation IMachineEventsStateProvider MachineEventsStateProvider { get; set; } /// <summary> + /// Prints the specified job. + /// The process parameters table will be calculated using color conversion gamut region. + /// This method cannot accept brush stops with 'Volume' as color space. + /// </summary> + /// <param name="job">The job.</param> + /// <returns></returns> + JobHandler Print(Job job); + + /// <summary> /// Prints the specified job using the specified job parameters. /// </summary> /// <param name="job">The job.</param> diff --git a/Software/Visual_Studio/Tango.Integration/Operation/JobHandler.cs b/Software/Visual_Studio/Tango.Integration/Operation/JobHandler.cs index 02c663e1b..02579b20a 100644 --- a/Software/Visual_Studio/Tango.Integration/Operation/JobHandler.cs +++ b/Software/Visual_Studio/Tango.Integration/Operation/JobHandler.cs @@ -116,7 +116,9 @@ namespace Tango.Integration.Operation Status = new RunningJobStatus(); Status.TotalTime = job.GetEstimatedDuration(processParameters); - Status.TotalProgress = Job.Length; + Status.RemainingUnits = job.NumberOfUnits; + Status.TotalProgress = Job.LengthIncludingNumberOfUnits; + Status.CurrentUnitTotalProgress = Job.Length; Status.Progress = 0; Status.RemainingTime = Status.TotalTime; Status.RemainingProgress = Status.TotalProgress; @@ -177,6 +179,8 @@ namespace Tango.Integration.Operation Status.Progress = s.Progress; Status.RemainingTime = Status.TotalTime - Job.TranslateProgressToTime(Status.Progress, ProcessParameters); Status.RemainingProgress = Status.TotalProgress - Status.Progress; + Status.RemainingUnits = Job.NumberOfUnits - (int)((Job.LengthIncludingNumberOfUnits / Status.CurrentUnitTotalProgress) * Status.Progress / Status.TotalProgress); + Status.CurrentUnitProgress = s.Progress - (Status.CurrentUnitTotalProgress * (Job.NumberOfUnits - Status.RemainingUnits)); if (s.Message != _lastStatusMessage && s.Message != String.Empty) { diff --git a/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs b/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs index 8cc2465b9..d75e2fc49 100644 --- a/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs +++ b/Software/Visual_Studio/Tango.Integration/Operation/MachineOperator.cs @@ -21,6 +21,8 @@ using Tango.BL.Entities; using Tango.PMR.Hardware; using Google.Protobuf; using Tango.PMR.Connection; +using Tango.BL.Enumerations; +using Tango.BL.ColorConversion; namespace Tango.Integration.Operation { @@ -471,6 +473,74 @@ namespace Tango.Integration.Operation #region Public Methods /// <summary> + /// Prints the specified job. + /// The process parameters table will be calculated using color conversion gamut region. + /// This method cannot accept brush stops with 'Volume' as color space. + /// </summary> + /// <param name="job">The job.</param> + /// <returns></returns> + public JobHandler Print(Job job) + { + //Check not brush stop has color space 'Volume'. + if (job.Segments.SelectMany(x => x.BrushStops).ToList().Exists(x => x.ColorSpace.Code == ColorSpaces.Volume.ToInt32())) + { + throw new InvalidOperationException("Cannot print a brush stop with volume color space when process parameters table has not been specified."); + } + + //Get least common process parameters table index. + int processParametersTableIndex = TangoColorConverter.GetLeastCommonProcessParametersTableIndex(job.Segments.SelectMany(x => x.BrushStops)); + + if (job.Rml == null) + { + throw new NullReferenceException("Job RML is null"); + } + + var processGroup = job.Rml.ProcessParametersTablesGroups.FirstOrDefault(x => x.Active); + + if (processGroup == null) + { + throw new NullReferenceException("Could not locate an active process parameters tables group for RML " + job.Rml.Name); + } + + var processParameters = processGroup.ProcessParametersTables.FirstOrDefault(x => x.TableIndex == processParametersTableIndex); + + if (processParameters == null) + { + throw new NullReferenceException("Could not locate process parameters table index " + processParametersTableIndex + " in group " + processGroup.Name + " for RML " + job.Rml.Name); + } + + //Perform color correction + foreach (var stop in job.Segments.SelectMany(x => x.BrushStops)) + { + if (stop.LiquidVolumes == null) + { + var suggestions = TangoColorConverter.GetSuggestions(stop); + + if (suggestions.OutOfGamut) + { + throw new InvalidOperationException("Cannot print a brush stop which is out of gamut."); + } + + stop.SetLiquidVolumes(job.Machine.Configuration, job.Rml, processParameters); + + foreach (var outputLiquid in suggestions.SingleCoordinates.OutputLiquids) + { + var liquidVolume = stop.LiquidVolumes.SingleOrDefault(x => x.IdsPack.LiquidType.Code == outputLiquid.LiquidType.ToInt32()); + + if (liquidVolume == null) + { + throw new NullReferenceException("Liquid volume not found for color conversion output liquid '" + outputLiquid.LiquidType + "'."); + } + + liquidVolume.Volume = outputLiquid.Volume; + } + } + } + + return Print(job, processParameters); + } + + /// <summary> /// Prints the specified job using the specified job parameters. /// </summary> /// <param name="job">The job.</param> @@ -478,10 +548,24 @@ namespace Tango.Integration.Operation /// <returns></returns> public JobHandler Print(Job job, ProcessParametersTable processParameters) { + var originalJob = job; + CurrentProcessParameters = processParameters; JobRequest request = new JobRequest(); + job = job.Clone(); + + var segments = job.Segments.ToList(); + + for (int i = 0; i < job.NumberOfUnits - 1; i++) + { + foreach (var s in segments) + { + job.Segments.Add(s); + } + } + JobTicket ticket = new JobTicket(); ticket.EnableInterSegment = job.EnableInterSegment; ticket.InterSegmentLength = job.InterSegmentLength; @@ -553,7 +637,7 @@ namespace Tango.Integration.Operation { LogManager.Log(ex, "Failed to cancel job."); } - }, job, processParameters); + }, originalJob, processParameters); LogRequestSent(request); bool responseLogged = false; diff --git a/Software/Visual_Studio/Tango.Integration/Operation/RunningJobStatus.cs b/Software/Visual_Studio/Tango.Integration/Operation/RunningJobStatus.cs index 335930277..17e5f3a40 100644 --- a/Software/Visual_Studio/Tango.Integration/Operation/RunningJobStatus.cs +++ b/Software/Visual_Studio/Tango.Integration/Operation/RunningJobStatus.cs @@ -37,6 +37,21 @@ namespace Tango.Integration.Operation public double RemainingProgress { get; set; } /// <summary> + /// Gets or sets the remaining units if it is an embroidery job. + /// </summary> + public int RemainingUnits { get; set; } + + /// <summary> + /// Gets or sets the current unit progress. + /// </summary> + public double CurrentUnitProgress { get; set; } + + /// <summary> + /// Gets or sets the current unit total progress. + /// </summary> + public double CurrentUnitTotalProgress { get; set; } + + /// <summary> /// Gets or sets a value indicating whether this instance is canceled. /// </summary> public bool IsCanceled { get; set; } diff --git a/Software/Visual_Studio/Tango.Settings/SettingsManager.cs b/Software/Visual_Studio/Tango.Settings/SettingsManager.cs index cd8b97c92..88a3f59e1 100644 --- a/Software/Visual_Studio/Tango.Settings/SettingsManager.cs +++ b/Software/Visual_Studio/Tango.Settings/SettingsManager.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using Tango.Logging; namespace Tango.Settings { @@ -37,6 +38,7 @@ namespace Tango.Settings private List<SettingsBase> _settingsCollection; private JsonSerializerSettings _jsonSettings; + private LogManager _logManager; private bool _loaded; /// <summary> @@ -54,6 +56,7 @@ namespace Tango.Settings /// </summary> private SettingsManager() { + _logManager = LogManager.Default; FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Twine", "Tango", "Settings", Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName) + ".json"); Folder = Path.GetDirectoryName(FilePath); Directory.CreateDirectory(Folder); @@ -88,12 +91,21 @@ namespace Tango.Settings /// <returns></returns> public T GetOrCreate<T>() where T : SettingsBase { - EnsureLoaded(); + try + { + _logManager.Log("Retrieving settings for " + typeof(T).Name); + EnsureLoaded(); + } + catch (Exception ex) + { + _logManager.Log(ex, "Error deserializing settings for " + typeof(T).Name); + } var settings = _settingsCollection.SingleOrDefault(x => x.GetType() == typeof(T)) as T; if (settings == null) { + _logManager.Log("Settings for " + typeof(T).Name + " were not found. Initializing default settings."); settings = Activator.CreateInstance<T>(); settings.SaveAction = Save; _settingsCollection.Add(settings); @@ -109,6 +121,8 @@ namespace Tango.Settings { if (File.Exists(FilePath)) { + _logManager.Log("Loading settings from " + FilePath + "..."); + _settingsCollection = JsonConvert.DeserializeObject<List<SettingsBase>>(File.ReadAllText(FilePath), _jsonSettings); foreach (var settings in _settingsCollection) @@ -135,6 +149,7 @@ namespace Tango.Settings { EnsureLoaded(); + _logManager.Log("Saving settings to " + FilePath + "..."); String json = JsonConvert.SerializeObject(_settingsCollection, _jsonSettings); File.WriteAllText(FilePath, json); } diff --git a/Software/Visual_Studio/Tango.Touch/Controls/TouchGifAnimation.cs b/Software/Visual_Studio/Tango.Touch/Controls/TouchGifAnimation.cs new file mode 100644 index 000000000..45e552a43 --- /dev/null +++ b/Software/Visual_Studio/Tango.Touch/Controls/TouchGifAnimation.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tango.Touch.Controls +{ + public class TouchGifAnimation : Control + { + public ImageSource Source + { + get { return (ImageSource)GetValue(SourceProperty); } + set { SetValue(SourceProperty, value); } + } + public static readonly DependencyProperty SourceProperty = + DependencyProperty.Register("Source", typeof(ImageSource), typeof(TouchGifAnimation), new PropertyMetadata(null)); + + public double SpeedRatio + { + get { return (double)GetValue(SpeedRatioProperty); } + set { SetValue(SpeedRatioProperty, value); } + } + public static readonly DependencyProperty SpeedRatioProperty = + DependencyProperty.Register("SpeedRatio", typeof(double), typeof(TouchGifAnimation), new PropertyMetadata(1.0)); + + public Stretch Stretch + { + get { return (Stretch)GetValue(StretchProperty); } + set { SetValue(StretchProperty, value); } + } + public static readonly DependencyProperty StretchProperty = + DependencyProperty.Register("Stretch", typeof(Stretch), typeof(TouchGifAnimation), new PropertyMetadata(Stretch.None)); + + public bool EnableAnimation + { + get { return (bool)GetValue(EnableAnimationProperty); } + set { SetValue(EnableAnimationProperty, value); } + } + public static readonly DependencyProperty EnableAnimationProperty = + DependencyProperty.Register("EnableAnimation", typeof(bool), typeof(TouchGifAnimation), new PropertyMetadata(false)); + + + static TouchGifAnimation() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(TouchGifAnimation), new FrameworkPropertyMetadata(typeof(TouchGifAnimation))); + } + } +} diff --git a/Software/Visual_Studio/Tango.Touch/Controls/TouchGifAnimation.xaml b/Software/Visual_Studio/Tango.Touch/Controls/TouchGifAnimation.xaml new file mode 100644 index 000000000..15154125e --- /dev/null +++ b/Software/Visual_Studio/Tango.Touch/Controls/TouchGifAnimation.xaml @@ -0,0 +1,21 @@ +<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:gif="clr-namespace:Tango.AnimatedGif;assembly=Tango.AnimatedGif" + xmlns:local="clr-namespace:Tango.Touch.Controls"> + + <Style TargetType="{x:Type local:TouchGifAnimation}"> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="{x:Type local:TouchGifAnimation}"> + <Image + Stretch="{TemplateBinding Stretch}" + gif:ImageBehavior.AnimatedSource="{TemplateBinding Source}" + gif:ImageBehavior.EnableAnimation="{TemplateBinding EnableAnimation}" + gif:ImageBehavior.SpeedRatio="{TemplateBinding SpeedRatio}" + /> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + +</ResourceDictionary>
\ No newline at end of file diff --git a/Software/Visual_Studio/Tango.Touch/Controls/TouchNumericTextBox.cs b/Software/Visual_Studio/Tango.Touch/Controls/TouchNumericTextBox.cs index c45701e99..aee5fae26 100644 --- a/Software/Visual_Studio/Tango.Touch/Controls/TouchNumericTextBox.cs +++ b/Software/Visual_Studio/Tango.Touch/Controls/TouchNumericTextBox.cs @@ -70,7 +70,7 @@ namespace Tango.Touch.Controls set { SetValue(MinimumProperty, value); } } public static readonly DependencyProperty MinimumProperty = - DependencyProperty.Register("Minimum", typeof(double), typeof(TouchNumericTextBox), new PropertyMetadata(.0)); + DependencyProperty.Register("Minimum", typeof(double), typeof(TouchNumericTextBox), new PropertyMetadata(0.0)); public double Maximum { @@ -108,6 +108,8 @@ namespace Tango.Touch.Controls _text_box.GotFocus += _text_box_GotFocus; _text_box.TextChanged += _text_box_TextChanged; + Value = Value; + OnValueChanged(); } diff --git a/Software/Visual_Studio/Tango.Touch/Controls/TouchToggleButton.cs b/Software/Visual_Studio/Tango.Touch/Controls/TouchToggleButton.cs index 8187645ca..f24d716db 100644 --- a/Software/Visual_Studio/Tango.Touch/Controls/TouchToggleButton.cs +++ b/Software/Visual_Studio/Tango.Touch/Controls/TouchToggleButton.cs @@ -19,6 +19,8 @@ namespace Tango.Touch.Controls public class TouchToggleButton : ToggleButton, ITouchControl { private Object _uncheckedContent; + private bool _loaded; + private bool first_checked; #region ITouchControl @@ -91,8 +93,33 @@ namespace Tango.Touch.Controls public TouchToggleButton() { - Loaded += (_, __) => _uncheckedContent = Content; - Checked += (_, __) => Content = CheckedContent != null ? CheckedContent : Content; + Loaded += (_, __) => + { + if (!_loaded) + { + _uncheckedContent = Content; + _loaded = true; + } + }; + + Checked += (_, __) => + { + if (_loaded) + { + Content = CheckedContent != null ? CheckedContent : Content; + } + else + { + Loaded += (x, y) => + { + if (!first_checked) + { + Content = CheckedContent != null ? CheckedContent : Content; + first_checked = true; + } + }; + } + }; Unchecked += (_, __) => Content = _uncheckedContent; } } diff --git a/Software/Visual_Studio/Tango.Touch/Tango.Touch.csproj b/Software/Visual_Studio/Tango.Touch/Tango.Touch.csproj index fada6cad8..e078d0887 100644 --- a/Software/Visual_Studio/Tango.Touch/Tango.Touch.csproj +++ b/Software/Visual_Studio/Tango.Touch/Tango.Touch.csproj @@ -68,6 +68,7 @@ <Compile Include="Controls\TouchCheckBox.cs" /> <Compile Include="Controls\TouchComboBox.cs" /> <Compile Include="Controls\TouchExpander.cs" /> + <Compile Include="Controls\TouchGifAnimation.cs" /> <Compile Include="Controls\TouchIcon.cs" /> <Compile Include="Controls\TouchIconKind.cs" /> <Compile Include="Controls\TouchImageButton.cs" /> @@ -118,6 +119,10 @@ <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> + <Page Include="Controls\TouchGifAnimation.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> <Page Include="Controls\TouchIcon.xaml"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> @@ -331,6 +336,10 @@ </None> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\Tango.AnimatedGif\Tango.AnimatedGif.csproj"> + <Project>{d129789c-3096-4d0b-8dd7-fe24a4df4b21}</Project> + <Name>Tango.AnimatedGif</Name> + </ProjectReference> <ProjectReference Include="..\Tango.Core\Tango.Core.csproj"> <Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project> <Name>Tango.Core</Name> diff --git a/Software/Visual_Studio/Tango.Touch/Themes/Generic.xaml b/Software/Visual_Studio/Tango.Touch/Themes/Generic.xaml index 971f3dd46..e077d1388 100644 --- a/Software/Visual_Studio/Tango.Touch/Themes/Generic.xaml +++ b/Software/Visual_Studio/Tango.Touch/Themes/Generic.xaml @@ -38,7 +38,8 @@ <ResourceDictionary Source="pack://application:,,,/Tango.Touch;component/Controls/TouchStaticListBox.xaml" /> <ResourceDictionary Source="pack://application:,,,/Tango.Touch;component/Controls/TouchRingProgress.xaml" /> <ResourceDictionary Source="pack://application:,,,/Tango.Touch;component/Controls/TouchNativeListBox.xaml" /> - + <ResourceDictionary Source="pack://application:,,,/Tango.Touch;component/Controls/TouchGifAnimation.xaml" /> + <ResourceDictionary Source="pack://application:,,,/Tango.Touch;component/Styles/TouchToggleButton.xaml" /> <ResourceDictionary Source="pack://application:,,,/Tango.Touch;component/Styles/TouchButton.xaml" /> diff --git a/Software/Visual_Studio/Tango.sln b/Software/Visual_Studio/Tango.sln index bfb278f0f..794b8be2d 100644 --- a/Software/Visual_Studio/Tango.sln +++ b/Software/Visual_Studio/Tango.sln @@ -210,6 +210,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.Hive", "Tango.Hive\Ta EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.CatalogGenerator", "Utilities\Tango.CatalogGenerator\Tango.CatalogGenerator.csproj", "{808E47B6-BAA7-4D23-83CB-40C53DC4B38A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tango.AnimatedGif", "Tango.AnimatedGif\Tango.AnimatedGif.csproj", "{D129789C-3096-4D0B-8DD7-FE24A4DF4B21}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AppVeyor|Any CPU = AppVeyor|Any CPU @@ -3569,6 +3571,46 @@ Global {808E47B6-BAA7-4D23-83CB-40C53DC4B38A}.Release|x64.Build.0 = Release|Any CPU {808E47B6-BAA7-4D23-83CB-40C53DC4B38A}.Release|x86.ActiveCfg = Release|Any CPU {808E47B6-BAA7-4D23-83CB-40C53DC4B38A}.Release|x86.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|Any CPU.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|Any CPU.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|ARM.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|ARM.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|ARM64.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|ARM64.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|x64.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|x64.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|x86.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.AppVeyor|x86.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|ARM.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|ARM64.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|x64.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|x64.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|x86.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Debug|x86.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|Any CPU.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|Any CPU.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|ARM.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|ARM.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|ARM64.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|ARM64.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|x64.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|x64.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|x86.ActiveCfg = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.DefaultBuild|x86.Build.0 = Debug|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|Any CPU.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|ARM.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|ARM.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|ARM64.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|ARM64.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|x64.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|x64.Build.0 = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|x86.ActiveCfg = Release|Any CPU + {D129789C-3096-4D0B-8DD7-FE24A4DF4B21}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Software/Visual_Studio/Utilities/Tango.UITests/App.xaml b/Software/Visual_Studio/Utilities/Tango.UITests/App.xaml index 0ede32b42..43576481f 100644 --- a/Software/Visual_Studio/Utilities/Tango.UITests/App.xaml +++ b/Software/Visual_Studio/Utilities/Tango.UITests/App.xaml @@ -6,20 +6,20 @@ StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> - <ResourceDictionary.MergedDictionaries> - <!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! --> + <!--<ResourceDictionary.MergedDictionaries> + --><!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! --><!-- <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" /> <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" /> <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" /> - <!-- Accent and AppTheme setting --> + --><!-- Accent and AppTheme setting --><!-- <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" /> <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/FlatButton.xaml" /> <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/VS/Colors.xaml" /> <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/VS/Styles.xaml" /> - </ResourceDictionary.MergedDictionaries> + </ResourceDictionary.MergedDictionaries>--> </ResourceDictionary> </Application.Resources> </Application> diff --git a/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml b/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml index 05bcad25c..6f821d30f 100644 --- a/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml +++ b/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml @@ -16,6 +16,6 @@ Title="MainWindow" Height="500" Width="800" Foreground="Red" DataContext="{Binding RelativeSource={RelativeSource Self}}"> <Grid> - <stubs:StubsView/> + <touch:TouchNumericTextBox Value="{Binding Value}" Minimum="1" Maximum="100" Margin="0 100 0 0" VerticalAlignment="Top" Width="100" /> </Grid> </Window> diff --git a/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml.cs b/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml.cs index 06613ee1e..c3eca4c76 100644 --- a/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml.cs +++ b/Software/Visual_Studio/Utilities/Tango.UITests/MainWindow.xaml.cs @@ -110,6 +110,14 @@ namespace Tango.UITests public RelayCommand<DropEventArgs> DropCommand { get; set; } + public int Value + { + get { return (int)GetValue(ValueProperty); } + set { SetValue(ValueProperty, value); } + } + public static readonly DependencyProperty ValueProperty = + DependencyProperty.Register("Value", typeof(int), typeof(MainWindow), new PropertyMetadata(0)); + public MainWindow() { Persons = new ObservableCollection<Person>(); @@ -127,9 +135,11 @@ namespace Tango.UITests DropCommand = new RelayCommand<DropEventArgs>(OnDrop); + Value = 10; + InitializeComponent(); - DataContext = new VM(); + //DataContext = new VM(); } private void OnDrop(DropEventArgs e) |
